From e142e60d9bb6719bdbf8bf5803de836d7ed5b60c Mon Sep 17 00:00:00 2001 From: thekovic <72971433+thekovic@users.noreply.github.com> Date: Fri, 10 Feb 2023 21:19:46 +0100 Subject: [PATCH] Doom Builder 64 1.5 --- .gitignore | 10 + Build/Builder64.default.cfg | 204 + Build/Compilers/Doom64/blam.cfg | 13 + Build/Compilers/Doom64/blam.exe | Bin 0 -> 26624 bytes Build/Compilers/Doom64/common.txt | 211 + Build/Compilers/Doom64/utils.txt | 60 + Build/Compilers/Nodebuilders/D64BSP.cfg | 31 + Build/Compilers/Nodebuilders/D64BSP.exe | Bin 0 -> 118784 bytes Build/Configurations/Doom64.cfg | 251 + Build/Configurations/Includes/Common.cfg | 54 + .../Configurations/Includes/D64_linedefs.cfg | 418 ++ Build/Configurations/Includes/D64_misc.cfg | 1086 ++++ Build/Configurations/Includes/D64_sectors.cfg | 15 + .../Includes/D64_texturesets.cfg | 172 + Build/Configurations/Includes/D64_things.cfg | 813 +++ .../Configurations/Includes/Doom2_things.cfg | 270 + Build/GPL.txt | 674 +++ Build/Refmanual.chm | Bin 0 -> 193207 bytes Build/Scintilla.dll | Bin 0 -> 442368 bytes Build/Scripting/Doom64_BLAM.cfg | 195 + Build/Sharpzip.dll | Bin 0 -> 180224 bytes Build/SlimDX.dll | Bin 0 -> 3414016 bytes Build/Sprites/Action.png | Bin 0 -> 471 bytes Build/Sprites/Arrow.png | Bin 0 -> 327 bytes Build/Sprites/Light.png | Bin 0 -> 509 bytes Build/Sprites/Sound.png | Bin 0 -> 587 bytes Builder.sln | 43 + Builder.suo | Bin 0 -> 100864 bytes Help/Contents.hhc | 212 + Help/Excluded/actions.html | 23 + Help/Index.hhk | 12 + Help/Refmanual.hhp | 31 + Help/a_template.html | 27 + Help/classiccontrols.html | 49 + Help/compilerconfigs.html | 24 + Help/configstructure.html | 262 + Help/configurations.html | 29 + Help/default.css | 149 + Help/e_brightness.html | 26 + Help/e_curvelinedefs.html | 38 + Help/e_drawgeometry.html | 58 + Help/e_editselection.html | 52 + Help/e_findreplace.html | 26 + Help/e_linedefs.html | 68 + Help/e_makesectors.html | 42 + Help/e_mapanalysis.html | 25 + Help/e_sectors.html | 88 + Help/e_things.html | 43 + Help/e_vertices.html | 43 + Help/e_visual.html | 181 + Help/editingmodes.html | 24 + Help/gameconfigs.html | 27 + Help/gc_basicsettings.html | 132 + Help/gc_formatsettings.html | 163 + Help/gc_resourcesettings.html | 126 + Help/header.gif | Bin 0 -> 2134 bytes Help/introduction.html | 26 + Help/scriptingconfigs.html | 24 + Help/scripts.js | 10 + Help/template.html | 23 + Help/terminology.html | 59 + Help/userinterface.html | 24 + Help/w_customfields.html | 24 + Help/w_errorsandwarnings.html | 27 + Help/w_gameconfigurations.html | 86 + Help/w_gridsetup.html | 27 + Help/w_imagesbrowser.html | 27 + Help/w_linedefedit.html | 48 + Help/w_mainwindow.html | 37 + Help/w_mainwindow.png | Bin 0 -> 64593 bytes Help/w_mapoptions.html | 30 + Help/w_openmapoptions.html | 28 + Help/w_preferences.html | 56 + Help/w_resourceoptions.html | 36 + Help/w_scripteditor.html | 51 + Help/w_scripteditor.png | Bin 0 -> 55223 bytes Help/w_sectoredit.html | 40 + Help/w_textureset.html | 29 + Help/w_thingedit.html | 36 + Help/w_thingsfilters.html | 27 + Help/w_vertexedit.html | 27 + LICENSE | 674 +++ README.md | 15 +- Source/Core/Actions/Action.cs | 290 + Source/Core/Actions/ActionAttribute.cs | 96 + Source/Core/Actions/ActionDelegate.cs | 30 + Source/Core/Actions/ActionManager.cs | 712 +++ Source/Core/Actions/BeginActionAttribute.cs | 43 + Source/Core/Actions/EndActionAttribute.cs | 43 + Source/Core/Actions/KeyControl.cs | 81 + Source/Core/Actions/MouseInput.cs | 152 + Source/Core/Actions/SpecialKeys.cs | 34 + Source/Core/Builder.csproj | 974 ++++ Source/Core/Compilers/AccCompiler.cs | 181 + Source/Core/Compilers/Compiler.cs | 217 + Source/Core/Compilers/CompilerError.cs | 66 + Source/Core/Compilers/MacroCompiler.cs | 157 + Source/Core/Compilers/NodesCompiler.cs | 125 + Source/Core/Config/AllTexturesSet.cs | 85 + Source/Core/Config/ArgumentInfo.cs | 119 + Source/Core/Config/CompilerInfo.cs | 102 + Source/Core/Config/ConfigurationInfo.cs | 299 + Source/Core/Config/DefinedTextureSet.cs | 110 + Source/Core/Config/EnumItem.cs | 86 + Source/Core/Config/EnumList.cs | 104 + Source/Core/Config/FlagTranslation.cs | 101 + Source/Core/Config/GameConfiguration.cs | 1029 ++++ Source/Core/Config/GeneralizedBit.cs | 66 + Source/Core/Config/GeneralizedCategory.cs | 99 + Source/Core/Config/GeneralizedOption.cs | 109 + Source/Core/Config/IFilledTextureSet.cs | 42 + Source/Core/Config/INumberedTitle.cs | 41 + Source/Core/Config/LinedefActionCategory.cs | 117 + Source/Core/Config/LinedefActionInfo.cs | 129 + Source/Core/Config/LinedefActivateInfo.cs | 94 + Source/Core/Config/MapLumpInfo.cs | 76 + Source/Core/Config/MatchingTextureSet.cs | 195 + Source/Core/Config/NodebuilderInfo.cs | 132 + Source/Core/Config/PasteOptions.cs | 104 + Source/Core/Config/ProgramConfiguration.cs | 584 ++ Source/Core/Config/ResourceTextureSet.cs | 125 + Source/Core/Config/ScriptConfiguration.cs | 257 + Source/Core/Config/SectorEffectInfo.cs | 96 + Source/Core/Config/SkillInfo.cs | 86 + Source/Core/Config/TagType.cs | 41 + Source/Core/Config/TextureIndexInfo.cs | 86 + Source/Core/Config/TextureSet.cs | 78 + Source/Core/Config/ThingCategory.cs | 216 + Source/Core/Config/ThingTypeInfo.cs | 311 + Source/Core/Config/ThingsFlagsCompare.cs | 316 ++ Source/Core/Config/UniversalFieldInfo.cs | 113 + .../ActionSelectorControl.Designer.cs | 101 + Source/Core/Controls/ActionSelectorControl.cs | 275 + .../Core/Controls/ActionSelectorControl.resx | 132 + Source/Core/Controls/AngleControl.Designer.cs | 173 + Source/Core/Controls/AngleControl.cs | 182 + Source/Core/Controls/AngleControl.resx | 147 + Source/Core/Controls/ArgumentBox.Designer.cs | 94 + Source/Core/Controls/ArgumentBox.cs | 286 + Source/Core/Controls/ArgumentBox.resx | 132 + Source/Core/Controls/AutoSelectTextbox.cs | 94 + .../ButtonsNumericTextbox.Designer.cs | 79 + Source/Core/Controls/ButtonsNumericTextbox.cs | 198 + .../Core/Controls/ButtonsNumericTextbox.resx | 129 + .../Controls/ButtonsNumericTextboxDesigner.cs | 75 + .../Controls/CheckboxArrayControl.Designer.cs | 51 + Source/Core/Controls/CheckboxArrayControl.cs | 135 + .../Core/Controls/CheckboxArrayControl.resx | 120 + Source/Core/Controls/ColorControl.Designer.cs | 101 + Source/Core/Controls/ColorControl.cs | 101 + Source/Core/Controls/ColorControl.resx | 123 + .../Controls/ColorControlSector.Designer.cs | 114 + Source/Core/Controls/ColorControlSector.cs | 131 + Source/Core/Controls/ColorControlSector.resx | 123 + Source/Core/Controls/Docker.cs | 82 + .../Core/Controls/DockersControl.Designer.cs | 94 + Source/Core/Controls/DockersControl.cs | 440 ++ Source/Core/Controls/DockersControl.resx | 129 + Source/Core/Controls/DockersTabsControl.cs | 315 ++ .../Controls/FieldsEditorControl.Designer.cs | 186 + Source/Core/Controls/FieldsEditorControl.cs | 751 +++ Source/Core/Controls/FieldsEditorControl.resx | 144 + Source/Core/Controls/FieldsEditorRow.cs | 281 + Source/Core/Controls/FlatSelectorControl.cs | 78 + .../Controls/ImageBrowserControl.Designer.cs | 168 + Source/Core/Controls/ImageBrowserControl.cs | 528 ++ Source/Core/Controls/ImageBrowserControl.resx | 147 + Source/Core/Controls/ImageBrowserItem.cs | 155 + .../Controls/ImageSelectorControl.Designer.cs | 85 + Source/Core/Controls/ImageSelectorControl.cs | 240 + .../Core/Controls/ImageSelectorControl.resx | 129 + .../Controls/LinedefInfoPanel.Designer.cs | 595 ++ Source/Core/Controls/LinedefInfoPanel.cs | 279 + Source/Core/Controls/LinedefInfoPanel.resx | 258 + Source/Core/Controls/NumericTextbox.cs | 281 + Source/Core/Controls/OptimizedListView.cs | 64 + .../Controls/PasteOptionsControl.Designer.cs | 148 + Source/Core/Controls/PasteOptionsControl.cs | 87 + Source/Core/Controls/PasteOptionsControl.resx | 144 + Source/Core/Controls/RenderTargetControl.cs | 154 + .../Controls/ResourceListEditor.Designer.cs | 229 + Source/Core/Controls/ResourceListEditor.cs | 373 ++ Source/Core/Controls/ResourceListEditor.resx | 254 + Source/Core/Controls/ResourceListView.cs | 286 + Source/Core/Controls/ScintillaConstants.cs | 432 ++ Source/Core/Controls/ScintillaControl.cs | 4982 +++++++++++++++++ Source/Core/Controls/ScriptDocumentTab.cs | 323 ++ .../Controls/ScriptEditorControl.Designer.cs | 167 + Source/Core/Controls/ScriptEditorControl.cs | 822 +++ Source/Core/Controls/ScriptEditorControl.resx | 120 + .../Controls/ScriptEditorPanel.Designer.cs | 404 ++ Source/Core/Controls/ScriptEditorPanel.cs | 807 +++ Source/Core/Controls/ScriptEditorPanel.resx | 182 + Source/Core/Controls/ScriptFileDocumentTab.cs | 233 + Source/Core/Controls/ScriptLumpDocumentTab.cs | 141 + .../Core/Controls/SectorInfoPanel.Designer.cs | 426 ++ Source/Core/Controls/SectorInfoPanel.cs | 105 + Source/Core/Controls/SectorInfoPanel.resx | 225 + .../Core/Controls/TextureSelectorControl.cs | 88 + .../Controls/ThingBrowserControl.Designer.cs | 212 + Source/Core/Controls/ThingBrowserControl.cs | 277 + Source/Core/Controls/ThingBrowserControl.resx | 558 ++ .../Core/Controls/ThingInfoPanel.Designer.cs | 354 ++ Source/Core/Controls/ThingInfoPanel.cs | 247 + Source/Core/Controls/ThingInfoPanel.resx | 210 + Source/Core/Controls/TransparentPanel.cs | 68 + Source/Core/Controls/TransparentTrackBar.cs | 41 + .../Core/Controls/VertexInfoPanel.Designer.cs | 89 + Source/Core/Controls/VertexInfoPanel.cs | 55 + Source/Core/Controls/VertexInfoPanel.resx | 135 + Source/Core/Data/ColorImage.cs | 105 + Source/Core/Data/ColormapImage.cs | 121 + Source/Core/Data/DataLocation.cs | 79 + Source/Core/Data/DataLocationList.cs | 110 + Source/Core/Data/DataManager.cs | 1413 +++++ Source/Core/Data/DataReader.cs | 161 + Source/Core/Data/DirectoryReader.cs | 334 ++ Source/Core/Data/FileImage.cs | 137 + Source/Core/Data/FlatImage.cs | 121 + Source/Core/Data/HighResImage.cs | 199 + Source/Core/Data/ImageData.cs | 437 ++ Source/Core/Data/ImageDataFormat.cs | 124 + Source/Core/Data/ImageLoadState.cs | 43 + Source/Core/Data/PK3FileImage.cs | 121 + Source/Core/Data/PK3Reader.cs | 383 ++ Source/Core/Data/PK3StructuredReader.cs | 467 ++ Source/Core/Data/PatchNames.cs | 86 + Source/Core/Data/Playpal.cs | 94 + Source/Core/Data/PreviewManager.cs | 284 + Source/Core/Data/ResourceImage.cs | 100 + Source/Core/Data/SimpleTextureImage.cs | 133 + Source/Core/Data/SpriteImage.cs | 142 + Source/Core/Data/TextureImage.cs | 168 + Source/Core/Data/TexturePatch.cs | 87 + Source/Core/Data/UnknownImage.cs | 82 + Source/Core/Data/WADReader.cs | 846 +++ Source/Core/Editing/ClassicMode.cs | 807 +++ Source/Core/Editing/CopyPasteManager.cs | 536 ++ Source/Core/Editing/CustomThingsFilter.cs | 79 + Source/Core/Editing/EditMode.cs | 221 + Source/Core/Editing/EditModeAttribute.cs | 136 + Source/Core/Editing/EditModeInfo.cs | 224 + Source/Core/Editing/EditingManager.cs | 445 ++ Source/Core/Editing/GridSetup.cs | 304 + Source/Core/Editing/NullThingsFilter.cs | 72 + Source/Core/Editing/ThingsFilter.cs | 274 + Source/Core/Editing/UndoManager.cs | 1309 +++++ Source/Core/Editing/UndoSnapshot.cs | 169 + Source/Core/General/BinaryHeap.cs | 260 + Source/Core/General/CRC.cs | 90 + Source/Core/General/Clock.cs | 162 + Source/Core/General/ErrorItem.cs | 55 + Source/Core/General/ErrorLogger.cs | 116 + Source/Core/General/General.cs | 1727 ++++++ Source/Core/General/Launcher.cs | 357 ++ Source/Core/General/MapManager.cs | 1689 ++++++ Source/Core/General/SavePurpose.cs | 52 + Source/Core/General/StepsList.cs | 99 + Source/Core/Geometry/Angle2D.cs | 93 + Source/Core/Geometry/DrawnVertex.cs | 44 + Source/Core/Geometry/EarClipPolygon.cs | 202 + Source/Core/Geometry/EarClipVertex.cs | 160 + Source/Core/Geometry/LabelPositionInfo.cs | 42 + Source/Core/Geometry/Line2D.cs | 281 + Source/Core/Geometry/LinedefAngleSorter.cs | 94 + Source/Core/Geometry/LinedefSide.cs | 117 + Source/Core/Geometry/LinedefsTracePath.cs | 134 + Source/Core/Geometry/Plane.cs | 142 + Source/Core/Geometry/ProjectedFrustum2D.cs | 156 + Source/Core/Geometry/SidedefAngleSorter.cs | 94 + Source/Core/Geometry/SidedefsTracePath.cs | 110 + Source/Core/Geometry/Tools.cs | 1805 ++++++ Source/Core/Geometry/Triangulation.cs | 971 ++++ Source/Core/Geometry/Vector2D.cs | 356 ++ Source/Core/Geometry/Vector3D.cs | 296 + Source/Core/IO/ClippedStream.cs | 275 + Source/Core/IO/Configuration.cs | 1505 +++++ Source/Core/IO/DeserializerStream.cs | 231 + Source/Core/IO/DirectoryFileEntry.cs | 90 + Source/Core/IO/DirectoryFilesList.cs | 301 + Source/Core/IO/Doom64MapSetIO.cs | 1251 +++++ Source/Core/IO/DoomColormapReader.cs | 242 + Source/Core/IO/DoomFlatReader.cs | 245 + Source/Core/IO/DoomMapSetIO.cs | 632 +++ Source/Core/IO/DoomPictureReader.cs | 278 + Source/Core/IO/FileImageReader.cs | 125 + Source/Core/IO/HexenMapSetIO.cs | 656 +++ Source/Core/IO/IImageReader.cs | 43 + Source/Core/IO/IMapSetIO.cs | 72 + Source/Core/IO/IReadWriteStream.cs | 89 + Source/Core/IO/Lump.cs | 191 + Source/Core/IO/MapSetIO.cs | 165 + Source/Core/IO/SerializerStream.cs | 237 + Source/Core/IO/UniversalCollection.cs | 45 + Source/Core/IO/UniversalEntry.cs | 73 + Source/Core/IO/UniversalMapSetIO.cs | 197 + Source/Core/IO/UniversalParser.cs | 838 +++ Source/Core/IO/UniversalStreamReader.cs | 517 ++ Source/Core/IO/UniversalStreamWriter.cs | 341 ++ Source/Core/IO/UnknownImageReader.cs | 115 + Source/Core/IO/WAD.cs | 410 ++ Source/Core/Map/BlockEntry.cs | 72 + Source/Core/Map/BlockMap.cs | 514 ++ Source/Core/Map/Lights.cs | 413 ++ Source/Core/Map/Linedef.cs | 1084 ++++ Source/Core/Map/Macro.cs | 133 + Source/Core/Map/MacroData.cs | 71 + Source/Core/Map/MapElement.cs | 132 + Source/Core/Map/MapElementCollection.cs | 82 + Source/Core/Map/MapOptions.cs | 321 ++ Source/Core/Map/MapSet.cs | 2936 ++++++++++ Source/Core/Map/Sector.cs | 744 +++ Source/Core/Map/SelectableElement.cs | 116 + Source/Core/Map/SelectionType.cs | 43 + Source/Core/Map/Sidedef.cs | 520 ++ Source/Core/Map/SidedefPart.cs | 36 + Source/Core/Map/Thing.cs | 497 ++ Source/Core/Map/UniFields.cs | 70 + Source/Core/Map/UniValue.cs | 184 + Source/Core/Map/Vertex.cs | 303 + Source/Core/Plugins/Plug.cs | 343 ++ Source/Core/Plugins/Plugin.cs | 215 + Source/Core/Plugins/PluginManager.cs | 334 ++ Source/Core/Properties/AssemblyInfo.cs | 32 + Source/Core/Properties/Resources.Designer.cs | 733 +++ Source/Core/Properties/Resources.resx | 322 ++ Source/Core/Rendering/ColorCollection.cs | 287 + Source/Core/Rendering/ColorSetting.cs | 95 + Source/Core/Rendering/D3DDevice.cs | 590 ++ Source/Core/Rendering/D3DShader.cs | 187 + Source/Core/Rendering/Display2DShader.cs | 189 + Source/Core/Rendering/FlatQuad.cs | 251 + Source/Core/Rendering/FlatVertex.cs | 53 + Source/Core/Rendering/ID3DResource.cs | 45 + Source/Core/Rendering/IRenderer2D.cs | 89 + Source/Core/Rendering/IRenderer3D.cs | 69 + Source/Core/Rendering/PixelColor.cs | 218 + Source/Core/Rendering/PixelColorBlock.cs | 92 + Source/Core/Rendering/Plotter.cs | 349 ++ Source/Core/Rendering/Presentation.cs | 160 + Source/Core/Rendering/RenderLayers.cs | 22 + Source/Core/Rendering/RenderPasses.cs | 48 + Source/Core/Rendering/Renderer.cs | 113 + Source/Core/Rendering/Renderer2D.cs | 1601 ++++++ Source/Core/Rendering/Renderer3D.cs | 1008 ++++ Source/Core/Rendering/ShaderManager.cs | 142 + Source/Core/Rendering/SurfaceBufferSet.cs | 59 + Source/Core/Rendering/SurfaceEntry.cs | 105 + Source/Core/Rendering/SurfaceManager.cs | 648 +++ Source/Core/Rendering/TextAlignment.cs | 56 + Source/Core/Rendering/TextFont.cs | 266 + Source/Core/Rendering/TextLabel.cs | 283 + Source/Core/Rendering/Things2DShader.cs | 183 + Source/Core/Rendering/ViewMode.cs | 50 + Source/Core/Rendering/World3DShader.cs | 260 + Source/Core/Rendering/WorldVertex.cs | 97 + Source/Core/Resources/Actions.cfg | 876 +++ Source/Core/Resources/Builder16.png | Bin 0 -> 1060 bytes Source/Core/Resources/CLogo.png | Bin 0 -> 5841 bytes Source/Core/Resources/Close.png | Bin 0 -> 264 bytes Source/Core/Resources/ColorPick.png | Bin 0 -> 997 bytes Source/Core/Resources/Copy.png | Bin 0 -> 643 bytes Source/Core/Resources/Crosshair.png | Bin 0 -> 9590 bytes Source/Core/Resources/CrosshairBusy.png | Bin 0 -> 2374 bytes Source/Core/Resources/Cut.png | Bin 0 -> 628 bytes Source/Core/Resources/DB64.ico | Bin 0 -> 88244 bytes Source/Core/Resources/ErrorLarge.png | Bin 0 -> 1016 bytes Source/Core/Resources/Failed.png | Bin 0 -> 5609 bytes Source/Core/Resources/Filter.png | Bin 0 -> 481 bytes Source/Core/Resources/Folder.png | Bin 0 -> 394 bytes Source/Core/Resources/Font.cfg | 710 +++ Source/Core/Resources/Font.png | Bin 0 -> 20550 bytes Source/Core/Resources/FullBrightness.png | Bin 0 -> 952 bytes Source/Core/Resources/Grid2.png | Bin 0 -> 173 bytes Source/Core/Resources/Grid2_arrowup.png | Bin 0 -> 191 bytes Source/Core/Resources/Grid4.png | Bin 0 -> 301 bytes Source/Core/Resources/Help.png | Bin 0 -> 367 bytes Source/Core/Resources/Hourglass.png | Bin 0 -> 507 bytes Source/Core/Resources/Hourglass3D.png | Bin 0 -> 463 bytes Source/Core/Resources/KnownTextureSet.png | Bin 0 -> 476 bytes Source/Core/Resources/Lexers.cfg | 93 + Source/Core/Resources/MissingTexture.png | Bin 0 -> 4303 bytes Source/Core/Resources/MissingTexture3D.png | Bin 0 -> 583 bytes Source/Core/Resources/Monster2.png | Bin 0 -> 880 bytes Source/Core/Resources/Monster3.png | Bin 0 -> 694 bytes Source/Core/Resources/NewMap.png | Bin 0 -> 263 bytes Source/Core/Resources/NewMap2.png | Bin 0 -> 329 bytes Source/Core/Resources/NewScript.png | Bin 0 -> 765 bytes Source/Core/Resources/Nothing.png | Bin 0 -> 128 bytes Source/Core/Resources/OpenMap.png | Bin 0 -> 418 bytes Source/Core/Resources/OpenScript.png | Bin 0 -> 934 bytes Source/Core/Resources/Paste.png | Bin 0 -> 730 bytes Source/Core/Resources/PasteSpecial.png | Bin 0 -> 735 bytes Source/Core/Resources/Prefab.png | Bin 0 -> 514 bytes Source/Core/Resources/Prefab2.png | Bin 0 -> 552 bytes Source/Core/Resources/Properties.png | Bin 0 -> 439 bytes Source/Core/Resources/Question.png | Bin 0 -> 960 bytes Source/Core/Resources/Redo.png | Bin 0 -> 436 bytes Source/Core/Resources/SaveAll.png | Bin 0 -> 609 bytes Source/Core/Resources/SaveMap.png | Bin 0 -> 439 bytes Source/Core/Resources/SaveScript.png | Bin 0 -> 923 bytes Source/Core/Resources/Script2.png | Bin 0 -> 845 bytes Source/Core/Resources/ScriptCompile.png | Bin 0 -> 900 bytes Source/Core/Resources/ScriptConstant.xpm | 95 + Source/Core/Resources/ScriptError.xpm | 100 + Source/Core/Resources/ScriptHelp.png | Bin 0 -> 965 bytes Source/Core/Resources/ScriptKeyword.xpm | 92 + Source/Core/Resources/ScriptPalette.png | Bin 0 -> 932 bytes Source/Core/Resources/SlimDX_small.png | Bin 0 -> 1053 bytes Source/Core/Resources/Splash3_small.png | Bin 0 -> 28183 bytes Source/Core/Resources/Splash3_trans.png | Bin 0 -> 426039 bytes Source/Core/Resources/Status0.png | Bin 0 -> 922 bytes Source/Core/Resources/Status1.png | Bin 0 -> 899 bytes Source/Core/Resources/Status10.png | Bin 0 -> 871 bytes Source/Core/Resources/Status11.png | Bin 0 -> 841 bytes Source/Core/Resources/Status12.png | Bin 0 -> 801 bytes Source/Core/Resources/Status2.png | Bin 0 -> 875 bytes Source/Core/Resources/Test.png | Bin 0 -> 450 bytes Source/Core/Resources/Thing2D_0.png | Bin 0 -> 4516 bytes Source/Core/Resources/Thing2D_1.png | Bin 0 -> 11522 bytes Source/Core/Resources/Thing2D_2.png | Bin 0 -> 3199 bytes Source/Core/Resources/Thing2D_3.png | Bin 0 -> 6272 bytes Source/Core/Resources/ThingBox.png | Bin 0 -> 482 bytes Source/Core/Resources/ThingCamera.png | Bin 0 -> 2197 bytes Source/Core/Resources/ThingTrigger.png | Bin 0 -> 705 bytes Source/Core/Resources/UDMF.cfg | 67 + Source/Core/Resources/Undo.png | Bin 0 -> 396 bytes Source/Core/Resources/UnknownImage.png | Bin 0 -> 586 bytes Source/Core/Resources/UnknownTexture3D.png | Bin 0 -> 1049 bytes Source/Core/Resources/UnknownThing.png | Bin 0 -> 150 bytes Source/Core/Resources/ViewColorCeiling.png | Bin 0 -> 1073 bytes Source/Core/Resources/ViewColorFloor.png | Bin 0 -> 1064 bytes Source/Core/Resources/ViewColorThing.png | Bin 0 -> 1077 bytes Source/Core/Resources/ViewNormal.png | Bin 0 -> 239 bytes Source/Core/Resources/ViewTextureCeiling.png | Bin 0 -> 559 bytes Source/Core/Resources/ViewTextureFloor.png | Bin 0 -> 562 bytes Source/Core/Resources/Warning.png | Bin 0 -> 725 bytes Source/Core/Resources/WarningLarge.png | Bin 0 -> 749 bytes Source/Core/Resources/WarningOff.png | Bin 0 -> 250 bytes Source/Core/Resources/White.png | Bin 0 -> 140 bytes Source/Core/Resources/Zoom.png | Bin 0 -> 194 bytes Source/Core/Resources/Zoom_arrowup.png | Bin 0 -> 207 bytes Source/Core/Resources/display2d.fx | 142 + Source/Core/Resources/mergegeometry.png | Bin 0 -> 530 bytes Source/Core/Resources/mergegeometry2.png | Bin 0 -> 517 bytes Source/Core/Resources/things2d.fx | 81 + Source/Core/Resources/treeview.png | Bin 0 -> 231 bytes Source/Core/Resources/world3d.fx | 134 + Source/Core/Types/AngleDegreesFloatHandler.cs | 114 + Source/Core/Types/AngleDegreesHandler.cs | 112 + Source/Core/Types/AngleRadiansHandler.cs | 113 + Source/Core/Types/BoolHandler.cs | 122 + Source/Core/Types/ColorHandler.cs | 126 + Source/Core/Types/EnumBitsHandler.cs | 122 + Source/Core/Types/EnumOptionHandler.cs | 194 + Source/Core/Types/EnumStringsHandler.cs | 176 + Source/Core/Types/FlatHandler.cs | 81 + Source/Core/Types/FloatHandler.cs | 98 + Source/Core/Types/IntegerHandler.cs | 103 + Source/Core/Types/LinedefTagHandler.cs | 40 + Source/Core/Types/LinedefTypeHandler.cs | 112 + Source/Core/Types/NullHandler.cs | 78 + Source/Core/Types/SectorEffectHandler.cs | 112 + Source/Core/Types/SectorTagHandler.cs | 40 + Source/Core/Types/StringHandler.cs | 88 + Source/Core/Types/TextureHandler.cs | 81 + Source/Core/Types/ThingTagHandler.cs | 40 + Source/Core/Types/TypeHandler.cs | 172 + Source/Core/Types/TypeHandlerAttribute.cs | 80 + Source/Core/Types/TypesManager.cs | 195 + Source/Core/Types/UniversalType.cs | 51 + Source/Core/VisualModes/IVisualPickable.cs | 49 + Source/Core/VisualModes/VisualBlockEntry.cs | 72 + Source/Core/VisualModes/VisualBlockMap.cs | 471 ++ Source/Core/VisualModes/VisualCamera.cs | 171 + Source/Core/VisualModes/VisualGeometry.cs | 195 + Source/Core/VisualModes/VisualMode.cs | 897 +++ Source/Core/VisualModes/VisualPickResult.cs | 48 + Source/Core/VisualModes/VisualSector.cs | 215 + Source/Core/VisualModes/VisualThing.cs | 288 + Source/Core/Windows/AboutForm.Designer.cs | 181 + Source/Core/Windows/AboutForm.cs | 67 + Source/Core/Windows/AboutForm.resx | 153 + .../Windows/ActionBrowserForm.Designer.cs | 505 ++ Source/Core/Windows/ActionBrowserForm.cs | 288 + Source/Core/Windows/ActionBrowserForm.resx | 306 + Source/Core/Windows/AngleForm.Designer.cs | 99 + Source/Core/Windows/AngleForm.cs | 109 + Source/Core/Windows/AngleForm.resx | 132 + Source/Core/Windows/BitFlagsForm.Designer.cs | 101 + Source/Core/Windows/BitFlagsForm.cs | 180 + Source/Core/Windows/BitFlagsForm.resx | 132 + Source/Core/Windows/ConfigForm.Designer.cs | 740 +++ Source/Core/Windows/ConfigForm.cs | 596 ++ Source/Core/Windows/ConfigForm.resx | 334 ++ .../Core/Windows/CustomFieldsForm.Designer.cs | 105 + Source/Core/Windows/CustomFieldsForm.cs | 113 + Source/Core/Windows/CustomFieldsForm.resx | 132 + Source/Core/Windows/DelayedForm.cs | 81 + Source/Core/Windows/DelayedForm.resx | 120 + .../Windows/EffectBrowserForm.Designer.cs | 396 ++ Source/Core/Windows/EffectBrowserForm.cs | 193 + Source/Core/Windows/EffectBrowserForm.resx | 195 + Source/Core/Windows/ErrorsForm.Designer.cs | 185 + Source/Core/Windows/ErrorsForm.cs | 148 + Source/Core/Windows/ErrorsForm.resx | 147 + Source/Core/Windows/FindReplaceOptions.cs | 49 + .../Core/Windows/FlatBrowserForm.Designer.cs | 166 + Source/Core/Windows/FlatBrowserForm.cs | 359 ++ Source/Core/Windows/FlatBrowserForm.resx | 199 + Source/Core/Windows/GridSetupForm.Designer.cs | 305 + Source/Core/Windows/GridSetupForm.cs | 185 + Source/Core/Windows/GridSetupForm.resx | 186 + Source/Core/Windows/IMainForm.cs | 172 + .../Core/Windows/LinedefEditForm.Designer.cs | 839 +++ Source/Core/Windows/LinedefEditForm.cs | 639 +++ Source/Core/Windows/LinedefEditForm.resx | 162 + Source/Core/Windows/MainForm.Designer.cs | 1874 +++++++ Source/Core/Windows/MainForm.cs | 2673 +++++++++ Source/Core/Windows/MainForm.resx | 1710 ++++++ .../Core/Windows/MapOptionsForm.Designer.cs | 224 + Source/Core/Windows/MapOptionsForm.cs | 247 + Source/Core/Windows/MapOptionsForm.resx | 174 + Source/Core/Windows/MessageBeepType.cs | 50 + .../Windows/OpenMapOptionsForm.Designer.cs | 216 + Source/Core/Windows/OpenMapOptionsForm.cs | 425 ++ Source/Core/Windows/OpenMapOptionsForm.resx | 165 + .../Core/Windows/PasteOptionsForm.Designer.cs | 99 + Source/Core/Windows/PasteOptionsForm.cs | 85 + Source/Core/Windows/PasteOptionsForm.resx | 132 + Source/Core/Windows/PreferencesController.cs | 109 + .../Core/Windows/PreferencesForm.Designer.cs | 1686 ++++++ Source/Core/Windows/PreferencesForm.cs | 785 +++ Source/Core/Windows/PreferencesForm.resx | 474 ++ .../Windows/ResourceOptionsForm.Designer.cs | 421 ++ Source/Core/Windows/ResourceOptionsForm.cs | 219 + Source/Core/Windows/ResourceOptionsForm.resx | 216 + .../Core/Windows/ScriptEditorForm.Designer.cs | 71 + Source/Core/Windows/ScriptEditorForm.cs | 194 + Source/Core/Windows/ScriptEditorForm.resx | 145 + .../Windows/ScriptFindReplaceForm.Designer.cs | 176 + Source/Core/Windows/ScriptFindReplaceForm.cs | 125 + .../Core/Windows/ScriptFindReplaceForm.resx | 153 + .../Core/Windows/SectorEditForm.Designer.cs | 681 +++ Source/Core/Windows/SectorEditForm.cs | 405 ++ Source/Core/Windows/SectorEditForm.resx | 174 + Source/Core/Windows/StatusInfo.cs | 82 + Source/Core/Windows/TanColorTable.cs | 786 +++ Source/Core/Windows/TextEditForm.Designer.cs | 112 + Source/Core/Windows/TextEditForm.cs | 82 + Source/Core/Windows/TextEditForm.resx | 132 + .../Windows/TextureBrowserForm.Designer.cs | 167 + Source/Core/Windows/TextureBrowserForm.cs | 360 ++ Source/Core/Windows/TextureBrowserForm.resx | 199 + .../Core/Windows/TextureSetForm.Designer.cs | 326 ++ Source/Core/Windows/TextureSetForm.cs | 232 + Source/Core/Windows/TextureSetForm.resx | 177 + .../Core/Windows/ThingBrowserForm.Designer.cs | 98 + Source/Core/Windows/ThingBrowserForm.cs | 94 + Source/Core/Windows/ThingBrowserForm.resx | 132 + Source/Core/Windows/ThingEditForm.Designer.cs | 318 ++ Source/Core/Windows/ThingEditForm.cs | 279 + Source/Core/Windows/ThingEditForm.resx | 177 + .../Windows/ThingsFiltersForm.Designer.cs | 237 + Source/Core/Windows/ThingsFiltersForm.cs | 296 + Source/Core/Windows/ThingsFiltersForm.resx | 159 + .../Core/Windows/VertexEditForm.Designer.cs | 222 + Source/Core/Windows/VertexEditForm.cs | 177 + Source/Core/Windows/VertexEditForm.resx | 165 + Source/Core/ZDoom/ActorStructure.cs | 549 ++ Source/Core/ZDoom/DecorateParser.cs | 243 + Source/Core/ZDoom/PatchStructure.cs | 223 + Source/Core/ZDoom/StateStructure.cs | 152 + Source/Core/ZDoom/TextureStructure.cs | 283 + Source/Core/ZDoom/TexturesParser.cs | 157 + Source/Core/ZDoom/ZDTextParser.cs | 326 ++ .../Plugins/BuilderModes/BuilderModes.csproj | 309 + .../ClassicModes/BaseClassicMode.cs | 209 + .../BuilderModes/ClassicModes/BlockMapMode.cs | 229 + .../ClassicModes/CurveLinedefsMode.cs | 296 + .../ClassicModes/DragGeometryMode.cs | 442 ++ .../ClassicModes/DragLinedefsMode.cs | 187 + .../ClassicModes/DragSectorsMode.cs | 193 + .../ClassicModes/DragThingsMode.cs | 417 ++ .../ClassicModes/DragVerticesMode.cs | 174 + .../ClassicModes/DrawGeometryMode.cs | 588 ++ .../ClassicModes/EditSelectionMode.cs | 1545 +++++ .../ClassicModes/ErrorCheckMode.cs | 165 + .../ClassicModes/FindReplaceMode.cs | 171 + .../BuilderModes/ClassicModes/LinedefsMode.cs | 911 +++ .../ClassicModes/MakeSectorMode.cs | 544 ++ .../BuilderModes/ClassicModes/SectorsMode.cs | 1438 +++++ .../BuilderModes/ClassicModes/ThingsMode.cs | 803 +++ .../ClassicModes/TriangulatorMode.cs | 584 ++ .../BuilderModes/ClassicModes/VerticesMode.cs | 721 +++ .../BuilderModes/ClassicModes/WAuthorMode.cs | 392 ++ .../ClassicModes/WAuthorTools.Designer.cs | 116 + .../BuilderModes/ClassicModes/WAuthorTools.cs | 21 + .../ClassicModes/WAuthorTools.resx | 123 + .../ErrorChecks/CheckClosedSectors.cs | 184 + .../ErrorChecks/CheckLineReferences.cs | 109 + .../ErrorChecks/CheckMissingTextures.cs | 117 + .../ErrorChecks/CheckOverlappingLines.cs | 137 + .../ErrorChecks/CheckStuckThings.cs | 276 + .../ErrorChecks/CheckUnknownFlats.cs | 97 + .../ErrorChecks/CheckUnknownTextures.cs | 110 + .../BuilderModes/ErrorChecks/ErrorChecker.cs | 113 + .../ErrorChecks/ErrorCheckerAttribute.cs | 83 + .../BuilderModes/ErrorChecks/ErrorResult.cs | 193 + .../ErrorChecks/ResultLineMissingFront.cs | 146 + .../ErrorChecks/ResultLineMissingSides.cs | 209 + .../ErrorChecks/ResultLineNotDoubleSided.cs | 145 + .../ErrorChecks/ResultLineNotSingleSided.cs | 108 + .../ErrorChecks/ResultLineOverlapping.cs | 92 + .../ErrorChecks/ResultNoErrors.cs | 72 + .../ErrorChecks/ResultSectorUnclosed.cs | 89 + .../ErrorChecks/ResultStuckThingInLine.cs | 91 + .../ErrorChecks/ResultStuckThingInThing.cs | 96 + .../ErrorChecks/ResultTextureMissing.cs | 120 + .../ErrorChecks/ResultThingOutside.cs | 95 + .../ErrorChecks/ResultUnknownFlat.cs | 107 + .../ErrorChecks/ResultUnknownTexture.cs | 134 + .../FindReplace/FindAnyTextureFlat.cs | 227 + .../FindReplace/FindLinedefNumber.cs | 139 + .../FindReplace/FindLinedefSectorRef.cs | 189 + .../FindReplace/FindLinedefTags.cs | 172 + .../FindReplace/FindLinedefThingRef.cs | 189 + .../FindReplace/FindLinedefTypes.cs | 167 + .../FindReplace/FindReplaceAttribute.cs | 80 + .../FindReplace/FindReplaceObject.cs | 120 + .../FindReplace/FindReplaceType.cs | 170 + .../FindReplace/FindSectorEffect.cs | 170 + .../FindReplace/FindSectorFlat.cs | 168 + .../FindReplace/FindSectorNumber.cs | 142 + .../FindReplace/FindSectorTags.cs | 167 + .../FindReplace/FindSidedefNumber.cs | 131 + .../FindReplace/FindSidedefTexture.cs | 173 + .../FindReplace/FindThingAction.cs | 174 + .../FindReplace/FindThingNumber.cs | 138 + .../FindReplace/FindThingSectorRef.cs | 189 + .../BuilderModes/FindReplace/FindThingTag.cs | 171 + .../FindReplace/FindThingThingRef.cs | 189 + .../BuilderModes/FindReplace/FindThingType.cs | 171 + .../FindReplace/FindVertexNumber.cs | 132 + .../BuilderModes/General/Association.cs | 87 + .../BuilderModes/General/BuilderPlug.cs | 481 ++ .../BuilderModes/General/CopyStructures.cs | 244 + .../BuilderModes/General/LineLengthLabel.cs | 123 + .../Plugins/BuilderModes/General/UndoGroup.cs | 42 + .../Interface/CurveLinedefsForm.Designer.cs | 253 + .../Interface/CurveLinedefsForm.cs | 213 + .../Interface/CurveLinedefsForm.resx | 165 + .../Interface/EditSelectionPanel.Designer.cs | 533 ++ .../Interface/EditSelectionPanel.cs | 223 + .../Interface/EditSelectionPanel.resx | 222 + .../Interface/ErrorCheckForm.Designer.cs | 208 + .../BuilderModes/Interface/ErrorCheckForm.cs | 479 ++ .../Interface/ErrorCheckForm.resx | 153 + .../Interface/FindReplaceForm.Designer.cs | 308 + .../BuilderModes/Interface/FindReplaceForm.cs | 418 ++ .../Interface/FindReplaceForm.resx | 183 + .../Interface/MakeDoorForm.Designer.cs | 156 + .../BuilderModes/Interface/MakeDoorForm.cs | 101 + .../BuilderModes/Interface/MakeDoorForm.resx | 147 + .../Interface/MenusForm.Designer.cs | 383 ++ .../BuilderModes/Interface/MenusForm.cs | 163 + .../BuilderModes/Interface/MenusForm.resx | 141 + .../Interface/PreferencesForm.Designer.cs | 397 ++ .../BuilderModes/Interface/PreferencesForm.cs | 120 + .../Interface/PreferencesForm.resx | 198 + .../Interface/UndoRedoPanel.Designer.cs | 76 + .../BuilderModes/Interface/UndoRedoPanel.cs | 320 ++ .../BuilderModes/Interface/UndoRedoPanel.resx | 120 + .../BuilderModes/Properties/AssemblyInfo.cs | 32 + .../Properties/Resources.Designer.cs | 192 + .../BuilderModes/Properties/Resources.resx | 151 + .../BuilderModes/Resources/Actions.cfg | 734 +++ .../BuilderModes/Resources/Blockmap.png | Bin 0 -> 171 bytes .../Resources/BrightnessGradient.png | Bin 0 -> 420 bytes .../BuilderModes/Resources/CeilsGradient.png | Bin 0 -> 406 bytes .../BuilderModes/Resources/CopyProperties.png | Bin 0 -> 657 bytes .../BuilderModes/Resources/CurveLines.png | Bin 0 -> 365 bytes .../BuilderModes/Resources/FindMode.png | Bin 0 -> 679 bytes .../BuilderModes/Resources/FlipSelectionH.png | Bin 0 -> 268 bytes .../BuilderModes/Resources/FlipSelectionV.png | Bin 0 -> 267 bytes .../BuilderModes/Resources/FloorsGradient.png | Bin 0 -> 354 bytes .../BuilderModes/Resources/HeightsMode.png | Bin 0 -> 379 bytes .../BuilderModes/Resources/LinesMode.png | Bin 0 -> 300 bytes .../Resources/MapAnalysisMode.png | Bin 0 -> 452 bytes .../BuilderModes/Resources/NewSector2.png | Bin 0 -> 345 bytes .../Resources/PasteProperties.png | Bin 0 -> 631 bytes .../BuilderModes/Resources/SectorsMode.png | Bin 0 -> 337 bytes .../BuilderModes/Resources/ThingsMode.png | Bin 0 -> 938 bytes .../Resources/TriangulatorMode.png | Bin 0 -> 924 bytes .../BuilderModes/Resources/VerticesMode.png | Bin 0 -> 212 bytes .../Resources/ViewSelectionIndex.png | Bin 0 -> 429 bytes .../BuilderModes/Resources/VisualMode.png | Bin 0 -> 794 bytes .../BuilderModes/Resources/WAuthor.png | Bin 0 -> 601 bytes .../BuilderModes/Resources/treeview.png | Bin 0 -> 231 bytes .../VisualModes/BaseVisualGeometrySector.cs | 349 ++ .../VisualModes/BaseVisualGeometrySidedef.cs | 726 +++ .../VisualModes/BaseVisualMode.cs | 1269 +++++ .../VisualModes/BaseVisualSector.cs | 209 + .../VisualModes/BaseVisualThing.cs | 496 ++ .../VisualModes/IVisualEventReceiver.cs | 76 + .../VisualModes/NullVisualEventReceiver.cs | 165 + .../VisualModes/VisualActionResult.cs | 46 + .../BuilderModes/VisualModes/VisualCeiling.cs | 205 + .../BuilderModes/VisualModes/VisualFloor.cs | 192 + .../BuilderModes/VisualModes/VisualLower.cs | 207 + .../VisualModes/VisualMiddleDouble.cs | 236 + .../VisualModes/VisualMiddleSingle.cs | 219 + .../VisualModes/VisualSidedefParts.cs | 74 + .../BuilderModes/VisualModes/VisualUpper.cs | 219 + .../Plugins/CopyPasteSectorProps/Actions.cfg | 62 + .../CopyPasteSectorProps/BuilderPlug.cs | 341 ++ .../CopyPasteSectorProperties.csproj | 74 + .../CopyPasteSectorProperties.suo | Bin 0 -> 2560 bytes .../CopyPasteSectorProps.txt | 30 + .../Properties/AssemblyInfo.cs | 32 + Source/Plugins/Statistics/Actions.cfg | 33 + Source/Plugins/Statistics/BuilderPlug.cs | 96 + .../Statistics/Properties/AssemblyInfo.cs | 32 + Source/Plugins/Statistics/Statistics.csproj | 90 + .../Statistics/StatisticsForm.Designer.cs | 196 + Source/Plugins/Statistics/StatisticsForm.cs | 88 + Source/Plugins/Statistics/StatisticsForm.resx | 156 + Source/Plugins/Statistics/StatisticsIcon.png | Bin 0 -> 280 bytes Source/Plugins/Statistics/StatisticsMode.cs | 184 + 729 files changed, 154559 insertions(+), 2 deletions(-) create mode 100644 .gitignore create mode 100644 Build/Builder64.default.cfg create mode 100644 Build/Compilers/Doom64/blam.cfg create mode 100644 Build/Compilers/Doom64/blam.exe create mode 100644 Build/Compilers/Doom64/common.txt create mode 100644 Build/Compilers/Doom64/utils.txt create mode 100644 Build/Compilers/Nodebuilders/D64BSP.cfg create mode 100644 Build/Compilers/Nodebuilders/D64BSP.exe create mode 100644 Build/Configurations/Doom64.cfg create mode 100644 Build/Configurations/Includes/Common.cfg create mode 100644 Build/Configurations/Includes/D64_linedefs.cfg create mode 100644 Build/Configurations/Includes/D64_misc.cfg create mode 100644 Build/Configurations/Includes/D64_sectors.cfg create mode 100644 Build/Configurations/Includes/D64_texturesets.cfg create mode 100644 Build/Configurations/Includes/D64_things.cfg create mode 100644 Build/Configurations/Includes/Doom2_things.cfg create mode 100644 Build/GPL.txt create mode 100644 Build/Refmanual.chm create mode 100644 Build/Scintilla.dll create mode 100644 Build/Scripting/Doom64_BLAM.cfg create mode 100644 Build/Sharpzip.dll create mode 100644 Build/SlimDX.dll create mode 100644 Build/Sprites/Action.png create mode 100644 Build/Sprites/Arrow.png create mode 100644 Build/Sprites/Light.png create mode 100644 Build/Sprites/Sound.png create mode 100644 Builder.sln create mode 100644 Builder.suo create mode 100644 Help/Contents.hhc create mode 100644 Help/Excluded/actions.html create mode 100644 Help/Index.hhk create mode 100644 Help/Refmanual.hhp create mode 100644 Help/a_template.html create mode 100644 Help/classiccontrols.html create mode 100644 Help/compilerconfigs.html create mode 100644 Help/configstructure.html create mode 100644 Help/configurations.html create mode 100644 Help/default.css create mode 100644 Help/e_brightness.html create mode 100644 Help/e_curvelinedefs.html create mode 100644 Help/e_drawgeometry.html create mode 100644 Help/e_editselection.html create mode 100644 Help/e_findreplace.html create mode 100644 Help/e_linedefs.html create mode 100644 Help/e_makesectors.html create mode 100644 Help/e_mapanalysis.html create mode 100644 Help/e_sectors.html create mode 100644 Help/e_things.html create mode 100644 Help/e_vertices.html create mode 100644 Help/e_visual.html create mode 100644 Help/editingmodes.html create mode 100644 Help/gameconfigs.html create mode 100644 Help/gc_basicsettings.html create mode 100644 Help/gc_formatsettings.html create mode 100644 Help/gc_resourcesettings.html create mode 100644 Help/header.gif create mode 100644 Help/introduction.html create mode 100644 Help/scriptingconfigs.html create mode 100644 Help/scripts.js create mode 100644 Help/template.html create mode 100644 Help/terminology.html create mode 100644 Help/userinterface.html create mode 100644 Help/w_customfields.html create mode 100644 Help/w_errorsandwarnings.html create mode 100644 Help/w_gameconfigurations.html create mode 100644 Help/w_gridsetup.html create mode 100644 Help/w_imagesbrowser.html create mode 100644 Help/w_linedefedit.html create mode 100644 Help/w_mainwindow.html create mode 100644 Help/w_mainwindow.png create mode 100644 Help/w_mapoptions.html create mode 100644 Help/w_openmapoptions.html create mode 100644 Help/w_preferences.html create mode 100644 Help/w_resourceoptions.html create mode 100644 Help/w_scripteditor.html create mode 100644 Help/w_scripteditor.png create mode 100644 Help/w_sectoredit.html create mode 100644 Help/w_textureset.html create mode 100644 Help/w_thingedit.html create mode 100644 Help/w_thingsfilters.html create mode 100644 Help/w_vertexedit.html create mode 100644 LICENSE create mode 100644 Source/Core/Actions/Action.cs create mode 100644 Source/Core/Actions/ActionAttribute.cs create mode 100644 Source/Core/Actions/ActionDelegate.cs create mode 100644 Source/Core/Actions/ActionManager.cs create mode 100644 Source/Core/Actions/BeginActionAttribute.cs create mode 100644 Source/Core/Actions/EndActionAttribute.cs create mode 100644 Source/Core/Actions/KeyControl.cs create mode 100644 Source/Core/Actions/MouseInput.cs create mode 100644 Source/Core/Actions/SpecialKeys.cs create mode 100644 Source/Core/Builder.csproj create mode 100644 Source/Core/Compilers/AccCompiler.cs create mode 100644 Source/Core/Compilers/Compiler.cs create mode 100644 Source/Core/Compilers/CompilerError.cs create mode 100644 Source/Core/Compilers/MacroCompiler.cs create mode 100644 Source/Core/Compilers/NodesCompiler.cs create mode 100644 Source/Core/Config/AllTexturesSet.cs create mode 100644 Source/Core/Config/ArgumentInfo.cs create mode 100644 Source/Core/Config/CompilerInfo.cs create mode 100644 Source/Core/Config/ConfigurationInfo.cs create mode 100644 Source/Core/Config/DefinedTextureSet.cs create mode 100644 Source/Core/Config/EnumItem.cs create mode 100644 Source/Core/Config/EnumList.cs create mode 100644 Source/Core/Config/FlagTranslation.cs create mode 100644 Source/Core/Config/GameConfiguration.cs create mode 100644 Source/Core/Config/GeneralizedBit.cs create mode 100644 Source/Core/Config/GeneralizedCategory.cs create mode 100644 Source/Core/Config/GeneralizedOption.cs create mode 100644 Source/Core/Config/IFilledTextureSet.cs create mode 100644 Source/Core/Config/INumberedTitle.cs create mode 100644 Source/Core/Config/LinedefActionCategory.cs create mode 100644 Source/Core/Config/LinedefActionInfo.cs create mode 100644 Source/Core/Config/LinedefActivateInfo.cs create mode 100644 Source/Core/Config/MapLumpInfo.cs create mode 100644 Source/Core/Config/MatchingTextureSet.cs create mode 100644 Source/Core/Config/NodebuilderInfo.cs create mode 100644 Source/Core/Config/PasteOptions.cs create mode 100644 Source/Core/Config/ProgramConfiguration.cs create mode 100644 Source/Core/Config/ResourceTextureSet.cs create mode 100644 Source/Core/Config/ScriptConfiguration.cs create mode 100644 Source/Core/Config/SectorEffectInfo.cs create mode 100644 Source/Core/Config/SkillInfo.cs create mode 100644 Source/Core/Config/TagType.cs create mode 100644 Source/Core/Config/TextureIndexInfo.cs create mode 100644 Source/Core/Config/TextureSet.cs create mode 100644 Source/Core/Config/ThingCategory.cs create mode 100644 Source/Core/Config/ThingTypeInfo.cs create mode 100644 Source/Core/Config/ThingsFlagsCompare.cs create mode 100644 Source/Core/Config/UniversalFieldInfo.cs create mode 100644 Source/Core/Controls/ActionSelectorControl.Designer.cs create mode 100644 Source/Core/Controls/ActionSelectorControl.cs create mode 100644 Source/Core/Controls/ActionSelectorControl.resx create mode 100644 Source/Core/Controls/AngleControl.Designer.cs create mode 100644 Source/Core/Controls/AngleControl.cs create mode 100644 Source/Core/Controls/AngleControl.resx create mode 100644 Source/Core/Controls/ArgumentBox.Designer.cs create mode 100644 Source/Core/Controls/ArgumentBox.cs create mode 100644 Source/Core/Controls/ArgumentBox.resx create mode 100644 Source/Core/Controls/AutoSelectTextbox.cs create mode 100644 Source/Core/Controls/ButtonsNumericTextbox.Designer.cs create mode 100644 Source/Core/Controls/ButtonsNumericTextbox.cs create mode 100644 Source/Core/Controls/ButtonsNumericTextbox.resx create mode 100644 Source/Core/Controls/ButtonsNumericTextboxDesigner.cs create mode 100644 Source/Core/Controls/CheckboxArrayControl.Designer.cs create mode 100644 Source/Core/Controls/CheckboxArrayControl.cs create mode 100644 Source/Core/Controls/CheckboxArrayControl.resx create mode 100644 Source/Core/Controls/ColorControl.Designer.cs create mode 100644 Source/Core/Controls/ColorControl.cs create mode 100644 Source/Core/Controls/ColorControl.resx create mode 100644 Source/Core/Controls/ColorControlSector.Designer.cs create mode 100644 Source/Core/Controls/ColorControlSector.cs create mode 100644 Source/Core/Controls/ColorControlSector.resx create mode 100644 Source/Core/Controls/Docker.cs create mode 100644 Source/Core/Controls/DockersControl.Designer.cs create mode 100644 Source/Core/Controls/DockersControl.cs create mode 100644 Source/Core/Controls/DockersControl.resx create mode 100644 Source/Core/Controls/DockersTabsControl.cs create mode 100644 Source/Core/Controls/FieldsEditorControl.Designer.cs create mode 100644 Source/Core/Controls/FieldsEditorControl.cs create mode 100644 Source/Core/Controls/FieldsEditorControl.resx create mode 100644 Source/Core/Controls/FieldsEditorRow.cs create mode 100644 Source/Core/Controls/FlatSelectorControl.cs create mode 100644 Source/Core/Controls/ImageBrowserControl.Designer.cs create mode 100644 Source/Core/Controls/ImageBrowserControl.cs create mode 100644 Source/Core/Controls/ImageBrowserControl.resx create mode 100644 Source/Core/Controls/ImageBrowserItem.cs create mode 100644 Source/Core/Controls/ImageSelectorControl.Designer.cs create mode 100644 Source/Core/Controls/ImageSelectorControl.cs create mode 100644 Source/Core/Controls/ImageSelectorControl.resx create mode 100644 Source/Core/Controls/LinedefInfoPanel.Designer.cs create mode 100644 Source/Core/Controls/LinedefInfoPanel.cs create mode 100644 Source/Core/Controls/LinedefInfoPanel.resx create mode 100644 Source/Core/Controls/NumericTextbox.cs create mode 100644 Source/Core/Controls/OptimizedListView.cs create mode 100644 Source/Core/Controls/PasteOptionsControl.Designer.cs create mode 100644 Source/Core/Controls/PasteOptionsControl.cs create mode 100644 Source/Core/Controls/PasteOptionsControl.resx create mode 100644 Source/Core/Controls/RenderTargetControl.cs create mode 100644 Source/Core/Controls/ResourceListEditor.Designer.cs create mode 100644 Source/Core/Controls/ResourceListEditor.cs create mode 100644 Source/Core/Controls/ResourceListEditor.resx create mode 100644 Source/Core/Controls/ResourceListView.cs create mode 100644 Source/Core/Controls/ScintillaConstants.cs create mode 100644 Source/Core/Controls/ScintillaControl.cs create mode 100644 Source/Core/Controls/ScriptDocumentTab.cs create mode 100644 Source/Core/Controls/ScriptEditorControl.Designer.cs create mode 100644 Source/Core/Controls/ScriptEditorControl.cs create mode 100644 Source/Core/Controls/ScriptEditorControl.resx create mode 100644 Source/Core/Controls/ScriptEditorPanel.Designer.cs create mode 100644 Source/Core/Controls/ScriptEditorPanel.cs create mode 100644 Source/Core/Controls/ScriptEditorPanel.resx create mode 100644 Source/Core/Controls/ScriptFileDocumentTab.cs create mode 100644 Source/Core/Controls/ScriptLumpDocumentTab.cs create mode 100644 Source/Core/Controls/SectorInfoPanel.Designer.cs create mode 100644 Source/Core/Controls/SectorInfoPanel.cs create mode 100644 Source/Core/Controls/SectorInfoPanel.resx create mode 100644 Source/Core/Controls/TextureSelectorControl.cs create mode 100644 Source/Core/Controls/ThingBrowserControl.Designer.cs create mode 100644 Source/Core/Controls/ThingBrowserControl.cs create mode 100644 Source/Core/Controls/ThingBrowserControl.resx create mode 100644 Source/Core/Controls/ThingInfoPanel.Designer.cs create mode 100644 Source/Core/Controls/ThingInfoPanel.cs create mode 100644 Source/Core/Controls/ThingInfoPanel.resx create mode 100644 Source/Core/Controls/TransparentPanel.cs create mode 100644 Source/Core/Controls/TransparentTrackBar.cs create mode 100644 Source/Core/Controls/VertexInfoPanel.Designer.cs create mode 100644 Source/Core/Controls/VertexInfoPanel.cs create mode 100644 Source/Core/Controls/VertexInfoPanel.resx create mode 100644 Source/Core/Data/ColorImage.cs create mode 100644 Source/Core/Data/ColormapImage.cs create mode 100644 Source/Core/Data/DataLocation.cs create mode 100644 Source/Core/Data/DataLocationList.cs create mode 100644 Source/Core/Data/DataManager.cs create mode 100644 Source/Core/Data/DataReader.cs create mode 100644 Source/Core/Data/DirectoryReader.cs create mode 100644 Source/Core/Data/FileImage.cs create mode 100644 Source/Core/Data/FlatImage.cs create mode 100644 Source/Core/Data/HighResImage.cs create mode 100644 Source/Core/Data/ImageData.cs create mode 100644 Source/Core/Data/ImageDataFormat.cs create mode 100644 Source/Core/Data/ImageLoadState.cs create mode 100644 Source/Core/Data/PK3FileImage.cs create mode 100644 Source/Core/Data/PK3Reader.cs create mode 100644 Source/Core/Data/PK3StructuredReader.cs create mode 100644 Source/Core/Data/PatchNames.cs create mode 100644 Source/Core/Data/Playpal.cs create mode 100644 Source/Core/Data/PreviewManager.cs create mode 100644 Source/Core/Data/ResourceImage.cs create mode 100644 Source/Core/Data/SimpleTextureImage.cs create mode 100644 Source/Core/Data/SpriteImage.cs create mode 100644 Source/Core/Data/TextureImage.cs create mode 100644 Source/Core/Data/TexturePatch.cs create mode 100644 Source/Core/Data/UnknownImage.cs create mode 100644 Source/Core/Data/WADReader.cs create mode 100644 Source/Core/Editing/ClassicMode.cs create mode 100644 Source/Core/Editing/CopyPasteManager.cs create mode 100644 Source/Core/Editing/CustomThingsFilter.cs create mode 100644 Source/Core/Editing/EditMode.cs create mode 100644 Source/Core/Editing/EditModeAttribute.cs create mode 100644 Source/Core/Editing/EditModeInfo.cs create mode 100644 Source/Core/Editing/EditingManager.cs create mode 100644 Source/Core/Editing/GridSetup.cs create mode 100644 Source/Core/Editing/NullThingsFilter.cs create mode 100644 Source/Core/Editing/ThingsFilter.cs create mode 100644 Source/Core/Editing/UndoManager.cs create mode 100644 Source/Core/Editing/UndoSnapshot.cs create mode 100644 Source/Core/General/BinaryHeap.cs create mode 100644 Source/Core/General/CRC.cs create mode 100644 Source/Core/General/Clock.cs create mode 100644 Source/Core/General/ErrorItem.cs create mode 100644 Source/Core/General/ErrorLogger.cs create mode 100644 Source/Core/General/General.cs create mode 100644 Source/Core/General/Launcher.cs create mode 100644 Source/Core/General/MapManager.cs create mode 100644 Source/Core/General/SavePurpose.cs create mode 100644 Source/Core/General/StepsList.cs create mode 100644 Source/Core/Geometry/Angle2D.cs create mode 100644 Source/Core/Geometry/DrawnVertex.cs create mode 100644 Source/Core/Geometry/EarClipPolygon.cs create mode 100644 Source/Core/Geometry/EarClipVertex.cs create mode 100644 Source/Core/Geometry/LabelPositionInfo.cs create mode 100644 Source/Core/Geometry/Line2D.cs create mode 100644 Source/Core/Geometry/LinedefAngleSorter.cs create mode 100644 Source/Core/Geometry/LinedefSide.cs create mode 100644 Source/Core/Geometry/LinedefsTracePath.cs create mode 100644 Source/Core/Geometry/Plane.cs create mode 100644 Source/Core/Geometry/ProjectedFrustum2D.cs create mode 100644 Source/Core/Geometry/SidedefAngleSorter.cs create mode 100644 Source/Core/Geometry/SidedefsTracePath.cs create mode 100644 Source/Core/Geometry/Tools.cs create mode 100644 Source/Core/Geometry/Triangulation.cs create mode 100644 Source/Core/Geometry/Vector2D.cs create mode 100644 Source/Core/Geometry/Vector3D.cs create mode 100644 Source/Core/IO/ClippedStream.cs create mode 100644 Source/Core/IO/Configuration.cs create mode 100644 Source/Core/IO/DeserializerStream.cs create mode 100644 Source/Core/IO/DirectoryFileEntry.cs create mode 100644 Source/Core/IO/DirectoryFilesList.cs create mode 100644 Source/Core/IO/Doom64MapSetIO.cs create mode 100644 Source/Core/IO/DoomColormapReader.cs create mode 100644 Source/Core/IO/DoomFlatReader.cs create mode 100644 Source/Core/IO/DoomMapSetIO.cs create mode 100644 Source/Core/IO/DoomPictureReader.cs create mode 100644 Source/Core/IO/FileImageReader.cs create mode 100644 Source/Core/IO/HexenMapSetIO.cs create mode 100644 Source/Core/IO/IImageReader.cs create mode 100644 Source/Core/IO/IMapSetIO.cs create mode 100644 Source/Core/IO/IReadWriteStream.cs create mode 100644 Source/Core/IO/Lump.cs create mode 100644 Source/Core/IO/MapSetIO.cs create mode 100644 Source/Core/IO/SerializerStream.cs create mode 100644 Source/Core/IO/UniversalCollection.cs create mode 100644 Source/Core/IO/UniversalEntry.cs create mode 100644 Source/Core/IO/UniversalMapSetIO.cs create mode 100644 Source/Core/IO/UniversalParser.cs create mode 100644 Source/Core/IO/UniversalStreamReader.cs create mode 100644 Source/Core/IO/UniversalStreamWriter.cs create mode 100644 Source/Core/IO/UnknownImageReader.cs create mode 100644 Source/Core/IO/WAD.cs create mode 100644 Source/Core/Map/BlockEntry.cs create mode 100644 Source/Core/Map/BlockMap.cs create mode 100644 Source/Core/Map/Lights.cs create mode 100644 Source/Core/Map/Linedef.cs create mode 100644 Source/Core/Map/Macro.cs create mode 100644 Source/Core/Map/MacroData.cs create mode 100644 Source/Core/Map/MapElement.cs create mode 100644 Source/Core/Map/MapElementCollection.cs create mode 100644 Source/Core/Map/MapOptions.cs create mode 100644 Source/Core/Map/MapSet.cs create mode 100644 Source/Core/Map/Sector.cs create mode 100644 Source/Core/Map/SelectableElement.cs create mode 100644 Source/Core/Map/SelectionType.cs create mode 100644 Source/Core/Map/Sidedef.cs create mode 100644 Source/Core/Map/SidedefPart.cs create mode 100644 Source/Core/Map/Thing.cs create mode 100644 Source/Core/Map/UniFields.cs create mode 100644 Source/Core/Map/UniValue.cs create mode 100644 Source/Core/Map/Vertex.cs create mode 100644 Source/Core/Plugins/Plug.cs create mode 100644 Source/Core/Plugins/Plugin.cs create mode 100644 Source/Core/Plugins/PluginManager.cs create mode 100644 Source/Core/Properties/AssemblyInfo.cs create mode 100644 Source/Core/Properties/Resources.Designer.cs create mode 100644 Source/Core/Properties/Resources.resx create mode 100644 Source/Core/Rendering/ColorCollection.cs create mode 100644 Source/Core/Rendering/ColorSetting.cs create mode 100644 Source/Core/Rendering/D3DDevice.cs create mode 100644 Source/Core/Rendering/D3DShader.cs create mode 100644 Source/Core/Rendering/Display2DShader.cs create mode 100644 Source/Core/Rendering/FlatQuad.cs create mode 100644 Source/Core/Rendering/FlatVertex.cs create mode 100644 Source/Core/Rendering/ID3DResource.cs create mode 100644 Source/Core/Rendering/IRenderer2D.cs create mode 100644 Source/Core/Rendering/IRenderer3D.cs create mode 100644 Source/Core/Rendering/PixelColor.cs create mode 100644 Source/Core/Rendering/PixelColorBlock.cs create mode 100644 Source/Core/Rendering/Plotter.cs create mode 100644 Source/Core/Rendering/Presentation.cs create mode 100644 Source/Core/Rendering/RenderLayers.cs create mode 100644 Source/Core/Rendering/RenderPasses.cs create mode 100644 Source/Core/Rendering/Renderer.cs create mode 100644 Source/Core/Rendering/Renderer2D.cs create mode 100644 Source/Core/Rendering/Renderer3D.cs create mode 100644 Source/Core/Rendering/ShaderManager.cs create mode 100644 Source/Core/Rendering/SurfaceBufferSet.cs create mode 100644 Source/Core/Rendering/SurfaceEntry.cs create mode 100644 Source/Core/Rendering/SurfaceManager.cs create mode 100644 Source/Core/Rendering/TextAlignment.cs create mode 100644 Source/Core/Rendering/TextFont.cs create mode 100644 Source/Core/Rendering/TextLabel.cs create mode 100644 Source/Core/Rendering/Things2DShader.cs create mode 100644 Source/Core/Rendering/ViewMode.cs create mode 100644 Source/Core/Rendering/World3DShader.cs create mode 100644 Source/Core/Rendering/WorldVertex.cs create mode 100644 Source/Core/Resources/Actions.cfg create mode 100644 Source/Core/Resources/Builder16.png create mode 100644 Source/Core/Resources/CLogo.png create mode 100644 Source/Core/Resources/Close.png create mode 100644 Source/Core/Resources/ColorPick.png create mode 100644 Source/Core/Resources/Copy.png create mode 100644 Source/Core/Resources/Crosshair.png create mode 100644 Source/Core/Resources/CrosshairBusy.png create mode 100644 Source/Core/Resources/Cut.png create mode 100644 Source/Core/Resources/DB64.ico create mode 100644 Source/Core/Resources/ErrorLarge.png create mode 100644 Source/Core/Resources/Failed.png create mode 100644 Source/Core/Resources/Filter.png create mode 100644 Source/Core/Resources/Folder.png create mode 100644 Source/Core/Resources/Font.cfg create mode 100644 Source/Core/Resources/Font.png create mode 100644 Source/Core/Resources/FullBrightness.png create mode 100644 Source/Core/Resources/Grid2.png create mode 100644 Source/Core/Resources/Grid2_arrowup.png create mode 100644 Source/Core/Resources/Grid4.png create mode 100644 Source/Core/Resources/Help.png create mode 100644 Source/Core/Resources/Hourglass.png create mode 100644 Source/Core/Resources/Hourglass3D.png create mode 100644 Source/Core/Resources/KnownTextureSet.png create mode 100644 Source/Core/Resources/Lexers.cfg create mode 100644 Source/Core/Resources/MissingTexture.png create mode 100644 Source/Core/Resources/MissingTexture3D.png create mode 100644 Source/Core/Resources/Monster2.png create mode 100644 Source/Core/Resources/Monster3.png create mode 100644 Source/Core/Resources/NewMap.png create mode 100644 Source/Core/Resources/NewMap2.png create mode 100644 Source/Core/Resources/NewScript.png create mode 100644 Source/Core/Resources/Nothing.png create mode 100644 Source/Core/Resources/OpenMap.png create mode 100644 Source/Core/Resources/OpenScript.png create mode 100644 Source/Core/Resources/Paste.png create mode 100644 Source/Core/Resources/PasteSpecial.png create mode 100644 Source/Core/Resources/Prefab.png create mode 100644 Source/Core/Resources/Prefab2.png create mode 100644 Source/Core/Resources/Properties.png create mode 100644 Source/Core/Resources/Question.png create mode 100644 Source/Core/Resources/Redo.png create mode 100644 Source/Core/Resources/SaveAll.png create mode 100644 Source/Core/Resources/SaveMap.png create mode 100644 Source/Core/Resources/SaveScript.png create mode 100644 Source/Core/Resources/Script2.png create mode 100644 Source/Core/Resources/ScriptCompile.png create mode 100644 Source/Core/Resources/ScriptConstant.xpm create mode 100644 Source/Core/Resources/ScriptError.xpm create mode 100644 Source/Core/Resources/ScriptHelp.png create mode 100644 Source/Core/Resources/ScriptKeyword.xpm create mode 100644 Source/Core/Resources/ScriptPalette.png create mode 100644 Source/Core/Resources/SlimDX_small.png create mode 100644 Source/Core/Resources/Splash3_small.png create mode 100644 Source/Core/Resources/Splash3_trans.png create mode 100644 Source/Core/Resources/Status0.png create mode 100644 Source/Core/Resources/Status1.png create mode 100644 Source/Core/Resources/Status10.png create mode 100644 Source/Core/Resources/Status11.png create mode 100644 Source/Core/Resources/Status12.png create mode 100644 Source/Core/Resources/Status2.png create mode 100644 Source/Core/Resources/Test.png create mode 100644 Source/Core/Resources/Thing2D_0.png create mode 100644 Source/Core/Resources/Thing2D_1.png create mode 100644 Source/Core/Resources/Thing2D_2.png create mode 100644 Source/Core/Resources/Thing2D_3.png create mode 100644 Source/Core/Resources/ThingBox.png create mode 100644 Source/Core/Resources/ThingCamera.png create mode 100644 Source/Core/Resources/ThingTrigger.png create mode 100644 Source/Core/Resources/UDMF.cfg create mode 100644 Source/Core/Resources/Undo.png create mode 100644 Source/Core/Resources/UnknownImage.png create mode 100644 Source/Core/Resources/UnknownTexture3D.png create mode 100644 Source/Core/Resources/UnknownThing.png create mode 100644 Source/Core/Resources/ViewColorCeiling.png create mode 100644 Source/Core/Resources/ViewColorFloor.png create mode 100644 Source/Core/Resources/ViewColorThing.png create mode 100644 Source/Core/Resources/ViewNormal.png create mode 100644 Source/Core/Resources/ViewTextureCeiling.png create mode 100644 Source/Core/Resources/ViewTextureFloor.png create mode 100644 Source/Core/Resources/Warning.png create mode 100644 Source/Core/Resources/WarningLarge.png create mode 100644 Source/Core/Resources/WarningOff.png create mode 100644 Source/Core/Resources/White.png create mode 100644 Source/Core/Resources/Zoom.png create mode 100644 Source/Core/Resources/Zoom_arrowup.png create mode 100644 Source/Core/Resources/display2d.fx create mode 100644 Source/Core/Resources/mergegeometry.png create mode 100644 Source/Core/Resources/mergegeometry2.png create mode 100644 Source/Core/Resources/things2d.fx create mode 100644 Source/Core/Resources/treeview.png create mode 100644 Source/Core/Resources/world3d.fx create mode 100644 Source/Core/Types/AngleDegreesFloatHandler.cs create mode 100644 Source/Core/Types/AngleDegreesHandler.cs create mode 100644 Source/Core/Types/AngleRadiansHandler.cs create mode 100644 Source/Core/Types/BoolHandler.cs create mode 100644 Source/Core/Types/ColorHandler.cs create mode 100644 Source/Core/Types/EnumBitsHandler.cs create mode 100644 Source/Core/Types/EnumOptionHandler.cs create mode 100644 Source/Core/Types/EnumStringsHandler.cs create mode 100644 Source/Core/Types/FlatHandler.cs create mode 100644 Source/Core/Types/FloatHandler.cs create mode 100644 Source/Core/Types/IntegerHandler.cs create mode 100644 Source/Core/Types/LinedefTagHandler.cs create mode 100644 Source/Core/Types/LinedefTypeHandler.cs create mode 100644 Source/Core/Types/NullHandler.cs create mode 100644 Source/Core/Types/SectorEffectHandler.cs create mode 100644 Source/Core/Types/SectorTagHandler.cs create mode 100644 Source/Core/Types/StringHandler.cs create mode 100644 Source/Core/Types/TextureHandler.cs create mode 100644 Source/Core/Types/ThingTagHandler.cs create mode 100644 Source/Core/Types/TypeHandler.cs create mode 100644 Source/Core/Types/TypeHandlerAttribute.cs create mode 100644 Source/Core/Types/TypesManager.cs create mode 100644 Source/Core/Types/UniversalType.cs create mode 100644 Source/Core/VisualModes/IVisualPickable.cs create mode 100644 Source/Core/VisualModes/VisualBlockEntry.cs create mode 100644 Source/Core/VisualModes/VisualBlockMap.cs create mode 100644 Source/Core/VisualModes/VisualCamera.cs create mode 100644 Source/Core/VisualModes/VisualGeometry.cs create mode 100644 Source/Core/VisualModes/VisualMode.cs create mode 100644 Source/Core/VisualModes/VisualPickResult.cs create mode 100644 Source/Core/VisualModes/VisualSector.cs create mode 100644 Source/Core/VisualModes/VisualThing.cs create mode 100644 Source/Core/Windows/AboutForm.Designer.cs create mode 100644 Source/Core/Windows/AboutForm.cs create mode 100644 Source/Core/Windows/AboutForm.resx create mode 100644 Source/Core/Windows/ActionBrowserForm.Designer.cs create mode 100644 Source/Core/Windows/ActionBrowserForm.cs create mode 100644 Source/Core/Windows/ActionBrowserForm.resx create mode 100644 Source/Core/Windows/AngleForm.Designer.cs create mode 100644 Source/Core/Windows/AngleForm.cs create mode 100644 Source/Core/Windows/AngleForm.resx create mode 100644 Source/Core/Windows/BitFlagsForm.Designer.cs create mode 100644 Source/Core/Windows/BitFlagsForm.cs create mode 100644 Source/Core/Windows/BitFlagsForm.resx create mode 100644 Source/Core/Windows/ConfigForm.Designer.cs create mode 100644 Source/Core/Windows/ConfigForm.cs create mode 100644 Source/Core/Windows/ConfigForm.resx create mode 100644 Source/Core/Windows/CustomFieldsForm.Designer.cs create mode 100644 Source/Core/Windows/CustomFieldsForm.cs create mode 100644 Source/Core/Windows/CustomFieldsForm.resx create mode 100644 Source/Core/Windows/DelayedForm.cs create mode 100644 Source/Core/Windows/DelayedForm.resx create mode 100644 Source/Core/Windows/EffectBrowserForm.Designer.cs create mode 100644 Source/Core/Windows/EffectBrowserForm.cs create mode 100644 Source/Core/Windows/EffectBrowserForm.resx create mode 100644 Source/Core/Windows/ErrorsForm.Designer.cs create mode 100644 Source/Core/Windows/ErrorsForm.cs create mode 100644 Source/Core/Windows/ErrorsForm.resx create mode 100644 Source/Core/Windows/FindReplaceOptions.cs create mode 100644 Source/Core/Windows/FlatBrowserForm.Designer.cs create mode 100644 Source/Core/Windows/FlatBrowserForm.cs create mode 100644 Source/Core/Windows/FlatBrowserForm.resx create mode 100644 Source/Core/Windows/GridSetupForm.Designer.cs create mode 100644 Source/Core/Windows/GridSetupForm.cs create mode 100644 Source/Core/Windows/GridSetupForm.resx create mode 100644 Source/Core/Windows/IMainForm.cs create mode 100644 Source/Core/Windows/LinedefEditForm.Designer.cs create mode 100644 Source/Core/Windows/LinedefEditForm.cs create mode 100644 Source/Core/Windows/LinedefEditForm.resx create mode 100644 Source/Core/Windows/MainForm.Designer.cs create mode 100644 Source/Core/Windows/MainForm.cs create mode 100644 Source/Core/Windows/MainForm.resx create mode 100644 Source/Core/Windows/MapOptionsForm.Designer.cs create mode 100644 Source/Core/Windows/MapOptionsForm.cs create mode 100644 Source/Core/Windows/MapOptionsForm.resx create mode 100644 Source/Core/Windows/MessageBeepType.cs create mode 100644 Source/Core/Windows/OpenMapOptionsForm.Designer.cs create mode 100644 Source/Core/Windows/OpenMapOptionsForm.cs create mode 100644 Source/Core/Windows/OpenMapOptionsForm.resx create mode 100644 Source/Core/Windows/PasteOptionsForm.Designer.cs create mode 100644 Source/Core/Windows/PasteOptionsForm.cs create mode 100644 Source/Core/Windows/PasteOptionsForm.resx create mode 100644 Source/Core/Windows/PreferencesController.cs create mode 100644 Source/Core/Windows/PreferencesForm.Designer.cs create mode 100644 Source/Core/Windows/PreferencesForm.cs create mode 100644 Source/Core/Windows/PreferencesForm.resx create mode 100644 Source/Core/Windows/ResourceOptionsForm.Designer.cs create mode 100644 Source/Core/Windows/ResourceOptionsForm.cs create mode 100644 Source/Core/Windows/ResourceOptionsForm.resx create mode 100644 Source/Core/Windows/ScriptEditorForm.Designer.cs create mode 100644 Source/Core/Windows/ScriptEditorForm.cs create mode 100644 Source/Core/Windows/ScriptEditorForm.resx create mode 100644 Source/Core/Windows/ScriptFindReplaceForm.Designer.cs create mode 100644 Source/Core/Windows/ScriptFindReplaceForm.cs create mode 100644 Source/Core/Windows/ScriptFindReplaceForm.resx create mode 100644 Source/Core/Windows/SectorEditForm.Designer.cs create mode 100644 Source/Core/Windows/SectorEditForm.cs create mode 100644 Source/Core/Windows/SectorEditForm.resx create mode 100644 Source/Core/Windows/StatusInfo.cs create mode 100644 Source/Core/Windows/TanColorTable.cs create mode 100644 Source/Core/Windows/TextEditForm.Designer.cs create mode 100644 Source/Core/Windows/TextEditForm.cs create mode 100644 Source/Core/Windows/TextEditForm.resx create mode 100644 Source/Core/Windows/TextureBrowserForm.Designer.cs create mode 100644 Source/Core/Windows/TextureBrowserForm.cs create mode 100644 Source/Core/Windows/TextureBrowserForm.resx create mode 100644 Source/Core/Windows/TextureSetForm.Designer.cs create mode 100644 Source/Core/Windows/TextureSetForm.cs create mode 100644 Source/Core/Windows/TextureSetForm.resx create mode 100644 Source/Core/Windows/ThingBrowserForm.Designer.cs create mode 100644 Source/Core/Windows/ThingBrowserForm.cs create mode 100644 Source/Core/Windows/ThingBrowserForm.resx create mode 100644 Source/Core/Windows/ThingEditForm.Designer.cs create mode 100644 Source/Core/Windows/ThingEditForm.cs create mode 100644 Source/Core/Windows/ThingEditForm.resx create mode 100644 Source/Core/Windows/ThingsFiltersForm.Designer.cs create mode 100644 Source/Core/Windows/ThingsFiltersForm.cs create mode 100644 Source/Core/Windows/ThingsFiltersForm.resx create mode 100644 Source/Core/Windows/VertexEditForm.Designer.cs create mode 100644 Source/Core/Windows/VertexEditForm.cs create mode 100644 Source/Core/Windows/VertexEditForm.resx create mode 100644 Source/Core/ZDoom/ActorStructure.cs create mode 100644 Source/Core/ZDoom/DecorateParser.cs create mode 100644 Source/Core/ZDoom/PatchStructure.cs create mode 100644 Source/Core/ZDoom/StateStructure.cs create mode 100644 Source/Core/ZDoom/TextureStructure.cs create mode 100644 Source/Core/ZDoom/TexturesParser.cs create mode 100644 Source/Core/ZDoom/ZDTextParser.cs create mode 100644 Source/Plugins/BuilderModes/BuilderModes.csproj create mode 100644 Source/Plugins/BuilderModes/ClassicModes/BaseClassicMode.cs create mode 100644 Source/Plugins/BuilderModes/ClassicModes/BlockMapMode.cs create mode 100644 Source/Plugins/BuilderModes/ClassicModes/CurveLinedefsMode.cs create mode 100644 Source/Plugins/BuilderModes/ClassicModes/DragGeometryMode.cs create mode 100644 Source/Plugins/BuilderModes/ClassicModes/DragLinedefsMode.cs create mode 100644 Source/Plugins/BuilderModes/ClassicModes/DragSectorsMode.cs create mode 100644 Source/Plugins/BuilderModes/ClassicModes/DragThingsMode.cs create mode 100644 Source/Plugins/BuilderModes/ClassicModes/DragVerticesMode.cs create mode 100644 Source/Plugins/BuilderModes/ClassicModes/DrawGeometryMode.cs create mode 100644 Source/Plugins/BuilderModes/ClassicModes/EditSelectionMode.cs create mode 100644 Source/Plugins/BuilderModes/ClassicModes/ErrorCheckMode.cs create mode 100644 Source/Plugins/BuilderModes/ClassicModes/FindReplaceMode.cs create mode 100644 Source/Plugins/BuilderModes/ClassicModes/LinedefsMode.cs create mode 100644 Source/Plugins/BuilderModes/ClassicModes/MakeSectorMode.cs create mode 100644 Source/Plugins/BuilderModes/ClassicModes/SectorsMode.cs create mode 100644 Source/Plugins/BuilderModes/ClassicModes/ThingsMode.cs create mode 100644 Source/Plugins/BuilderModes/ClassicModes/TriangulatorMode.cs create mode 100644 Source/Plugins/BuilderModes/ClassicModes/VerticesMode.cs create mode 100644 Source/Plugins/BuilderModes/ClassicModes/WAuthorMode.cs create mode 100644 Source/Plugins/BuilderModes/ClassicModes/WAuthorTools.Designer.cs create mode 100644 Source/Plugins/BuilderModes/ClassicModes/WAuthorTools.cs create mode 100644 Source/Plugins/BuilderModes/ClassicModes/WAuthorTools.resx create mode 100644 Source/Plugins/BuilderModes/ErrorChecks/CheckClosedSectors.cs create mode 100644 Source/Plugins/BuilderModes/ErrorChecks/CheckLineReferences.cs create mode 100644 Source/Plugins/BuilderModes/ErrorChecks/CheckMissingTextures.cs create mode 100644 Source/Plugins/BuilderModes/ErrorChecks/CheckOverlappingLines.cs create mode 100644 Source/Plugins/BuilderModes/ErrorChecks/CheckStuckThings.cs create mode 100644 Source/Plugins/BuilderModes/ErrorChecks/CheckUnknownFlats.cs create mode 100644 Source/Plugins/BuilderModes/ErrorChecks/CheckUnknownTextures.cs create mode 100644 Source/Plugins/BuilderModes/ErrorChecks/ErrorChecker.cs create mode 100644 Source/Plugins/BuilderModes/ErrorChecks/ErrorCheckerAttribute.cs create mode 100644 Source/Plugins/BuilderModes/ErrorChecks/ErrorResult.cs create mode 100644 Source/Plugins/BuilderModes/ErrorChecks/ResultLineMissingFront.cs create mode 100644 Source/Plugins/BuilderModes/ErrorChecks/ResultLineMissingSides.cs create mode 100644 Source/Plugins/BuilderModes/ErrorChecks/ResultLineNotDoubleSided.cs create mode 100644 Source/Plugins/BuilderModes/ErrorChecks/ResultLineNotSingleSided.cs create mode 100644 Source/Plugins/BuilderModes/ErrorChecks/ResultLineOverlapping.cs create mode 100644 Source/Plugins/BuilderModes/ErrorChecks/ResultNoErrors.cs create mode 100644 Source/Plugins/BuilderModes/ErrorChecks/ResultSectorUnclosed.cs create mode 100644 Source/Plugins/BuilderModes/ErrorChecks/ResultStuckThingInLine.cs create mode 100644 Source/Plugins/BuilderModes/ErrorChecks/ResultStuckThingInThing.cs create mode 100644 Source/Plugins/BuilderModes/ErrorChecks/ResultTextureMissing.cs create mode 100644 Source/Plugins/BuilderModes/ErrorChecks/ResultThingOutside.cs create mode 100644 Source/Plugins/BuilderModes/ErrorChecks/ResultUnknownFlat.cs create mode 100644 Source/Plugins/BuilderModes/ErrorChecks/ResultUnknownTexture.cs create mode 100644 Source/Plugins/BuilderModes/FindReplace/FindAnyTextureFlat.cs create mode 100644 Source/Plugins/BuilderModes/FindReplace/FindLinedefNumber.cs create mode 100644 Source/Plugins/BuilderModes/FindReplace/FindLinedefSectorRef.cs create mode 100644 Source/Plugins/BuilderModes/FindReplace/FindLinedefTags.cs create mode 100644 Source/Plugins/BuilderModes/FindReplace/FindLinedefThingRef.cs create mode 100644 Source/Plugins/BuilderModes/FindReplace/FindLinedefTypes.cs create mode 100644 Source/Plugins/BuilderModes/FindReplace/FindReplaceAttribute.cs create mode 100644 Source/Plugins/BuilderModes/FindReplace/FindReplaceObject.cs create mode 100644 Source/Plugins/BuilderModes/FindReplace/FindReplaceType.cs create mode 100644 Source/Plugins/BuilderModes/FindReplace/FindSectorEffect.cs create mode 100644 Source/Plugins/BuilderModes/FindReplace/FindSectorFlat.cs create mode 100644 Source/Plugins/BuilderModes/FindReplace/FindSectorNumber.cs create mode 100644 Source/Plugins/BuilderModes/FindReplace/FindSectorTags.cs create mode 100644 Source/Plugins/BuilderModes/FindReplace/FindSidedefNumber.cs create mode 100644 Source/Plugins/BuilderModes/FindReplace/FindSidedefTexture.cs create mode 100644 Source/Plugins/BuilderModes/FindReplace/FindThingAction.cs create mode 100644 Source/Plugins/BuilderModes/FindReplace/FindThingNumber.cs create mode 100644 Source/Plugins/BuilderModes/FindReplace/FindThingSectorRef.cs create mode 100644 Source/Plugins/BuilderModes/FindReplace/FindThingTag.cs create mode 100644 Source/Plugins/BuilderModes/FindReplace/FindThingThingRef.cs create mode 100644 Source/Plugins/BuilderModes/FindReplace/FindThingType.cs create mode 100644 Source/Plugins/BuilderModes/FindReplace/FindVertexNumber.cs create mode 100644 Source/Plugins/BuilderModes/General/Association.cs create mode 100644 Source/Plugins/BuilderModes/General/BuilderPlug.cs create mode 100644 Source/Plugins/BuilderModes/General/CopyStructures.cs create mode 100644 Source/Plugins/BuilderModes/General/LineLengthLabel.cs create mode 100644 Source/Plugins/BuilderModes/General/UndoGroup.cs create mode 100644 Source/Plugins/BuilderModes/Interface/CurveLinedefsForm.Designer.cs create mode 100644 Source/Plugins/BuilderModes/Interface/CurveLinedefsForm.cs create mode 100644 Source/Plugins/BuilderModes/Interface/CurveLinedefsForm.resx create mode 100644 Source/Plugins/BuilderModes/Interface/EditSelectionPanel.Designer.cs create mode 100644 Source/Plugins/BuilderModes/Interface/EditSelectionPanel.cs create mode 100644 Source/Plugins/BuilderModes/Interface/EditSelectionPanel.resx create mode 100644 Source/Plugins/BuilderModes/Interface/ErrorCheckForm.Designer.cs create mode 100644 Source/Plugins/BuilderModes/Interface/ErrorCheckForm.cs create mode 100644 Source/Plugins/BuilderModes/Interface/ErrorCheckForm.resx create mode 100644 Source/Plugins/BuilderModes/Interface/FindReplaceForm.Designer.cs create mode 100644 Source/Plugins/BuilderModes/Interface/FindReplaceForm.cs create mode 100644 Source/Plugins/BuilderModes/Interface/FindReplaceForm.resx create mode 100644 Source/Plugins/BuilderModes/Interface/MakeDoorForm.Designer.cs create mode 100644 Source/Plugins/BuilderModes/Interface/MakeDoorForm.cs create mode 100644 Source/Plugins/BuilderModes/Interface/MakeDoorForm.resx create mode 100644 Source/Plugins/BuilderModes/Interface/MenusForm.Designer.cs create mode 100644 Source/Plugins/BuilderModes/Interface/MenusForm.cs create mode 100644 Source/Plugins/BuilderModes/Interface/MenusForm.resx create mode 100644 Source/Plugins/BuilderModes/Interface/PreferencesForm.Designer.cs create mode 100644 Source/Plugins/BuilderModes/Interface/PreferencesForm.cs create mode 100644 Source/Plugins/BuilderModes/Interface/PreferencesForm.resx create mode 100644 Source/Plugins/BuilderModes/Interface/UndoRedoPanel.Designer.cs create mode 100644 Source/Plugins/BuilderModes/Interface/UndoRedoPanel.cs create mode 100644 Source/Plugins/BuilderModes/Interface/UndoRedoPanel.resx create mode 100644 Source/Plugins/BuilderModes/Properties/AssemblyInfo.cs create mode 100644 Source/Plugins/BuilderModes/Properties/Resources.Designer.cs create mode 100644 Source/Plugins/BuilderModes/Properties/Resources.resx create mode 100644 Source/Plugins/BuilderModes/Resources/Actions.cfg create mode 100644 Source/Plugins/BuilderModes/Resources/Blockmap.png create mode 100644 Source/Plugins/BuilderModes/Resources/BrightnessGradient.png create mode 100644 Source/Plugins/BuilderModes/Resources/CeilsGradient.png create mode 100644 Source/Plugins/BuilderModes/Resources/CopyProperties.png create mode 100644 Source/Plugins/BuilderModes/Resources/CurveLines.png create mode 100644 Source/Plugins/BuilderModes/Resources/FindMode.png create mode 100644 Source/Plugins/BuilderModes/Resources/FlipSelectionH.png create mode 100644 Source/Plugins/BuilderModes/Resources/FlipSelectionV.png create mode 100644 Source/Plugins/BuilderModes/Resources/FloorsGradient.png create mode 100644 Source/Plugins/BuilderModes/Resources/HeightsMode.png create mode 100644 Source/Plugins/BuilderModes/Resources/LinesMode.png create mode 100644 Source/Plugins/BuilderModes/Resources/MapAnalysisMode.png create mode 100644 Source/Plugins/BuilderModes/Resources/NewSector2.png create mode 100644 Source/Plugins/BuilderModes/Resources/PasteProperties.png create mode 100644 Source/Plugins/BuilderModes/Resources/SectorsMode.png create mode 100644 Source/Plugins/BuilderModes/Resources/ThingsMode.png create mode 100644 Source/Plugins/BuilderModes/Resources/TriangulatorMode.png create mode 100644 Source/Plugins/BuilderModes/Resources/VerticesMode.png create mode 100644 Source/Plugins/BuilderModes/Resources/ViewSelectionIndex.png create mode 100644 Source/Plugins/BuilderModes/Resources/VisualMode.png create mode 100644 Source/Plugins/BuilderModes/Resources/WAuthor.png create mode 100644 Source/Plugins/BuilderModes/Resources/treeview.png create mode 100644 Source/Plugins/BuilderModes/VisualModes/BaseVisualGeometrySector.cs create mode 100644 Source/Plugins/BuilderModes/VisualModes/BaseVisualGeometrySidedef.cs create mode 100644 Source/Plugins/BuilderModes/VisualModes/BaseVisualMode.cs create mode 100644 Source/Plugins/BuilderModes/VisualModes/BaseVisualSector.cs create mode 100644 Source/Plugins/BuilderModes/VisualModes/BaseVisualThing.cs create mode 100644 Source/Plugins/BuilderModes/VisualModes/IVisualEventReceiver.cs create mode 100644 Source/Plugins/BuilderModes/VisualModes/NullVisualEventReceiver.cs create mode 100644 Source/Plugins/BuilderModes/VisualModes/VisualActionResult.cs create mode 100644 Source/Plugins/BuilderModes/VisualModes/VisualCeiling.cs create mode 100644 Source/Plugins/BuilderModes/VisualModes/VisualFloor.cs create mode 100644 Source/Plugins/BuilderModes/VisualModes/VisualLower.cs create mode 100644 Source/Plugins/BuilderModes/VisualModes/VisualMiddleDouble.cs create mode 100644 Source/Plugins/BuilderModes/VisualModes/VisualMiddleSingle.cs create mode 100644 Source/Plugins/BuilderModes/VisualModes/VisualSidedefParts.cs create mode 100644 Source/Plugins/BuilderModes/VisualModes/VisualUpper.cs create mode 100644 Source/Plugins/CopyPasteSectorProps/Actions.cfg create mode 100644 Source/Plugins/CopyPasteSectorProps/BuilderPlug.cs create mode 100644 Source/Plugins/CopyPasteSectorProps/CopyPasteSectorProperties.csproj create mode 100644 Source/Plugins/CopyPasteSectorProps/CopyPasteSectorProperties.suo create mode 100644 Source/Plugins/CopyPasteSectorProps/CopyPasteSectorProps.txt create mode 100644 Source/Plugins/CopyPasteSectorProps/Properties/AssemblyInfo.cs create mode 100644 Source/Plugins/Statistics/Actions.cfg create mode 100644 Source/Plugins/Statistics/BuilderPlug.cs create mode 100644 Source/Plugins/Statistics/Properties/AssemblyInfo.cs create mode 100644 Source/Plugins/Statistics/Statistics.csproj create mode 100644 Source/Plugins/Statistics/StatisticsForm.Designer.cs create mode 100644 Source/Plugins/Statistics/StatisticsForm.cs create mode 100644 Source/Plugins/Statistics/StatisticsForm.resx create mode 100644 Source/Plugins/Statistics/StatisticsIcon.png create mode 100644 Source/Plugins/Statistics/StatisticsMode.cs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..73b3cac --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +.vs +Source/Core/obj +Source/Plugins/BuilderModes/obj +Source/Plugins/Statistics/obj +Source/Plugins/CopyPasteSectorProps/obj +Source/Plugins/ImageDrawingExample/obj +*.zip +Build/*.exe +Build/*.pdb +Build/Plugins diff --git a/Build/Builder64.default.cfg b/Build/Builder64.default.cfg new file mode 100644 index 0000000..519415c --- /dev/null +++ b/Build/Builder64.default.cfg @@ -0,0 +1,204 @@ +shortcuts +{ + builder_closemap = 0; + builder_configuration = 117; + builder_newmap = 131150; + builder_openmap = 131151; + builder_scrolleast = 39; + builder_scrollnorth = 38; + builder_scrollsouth = 40; + builder_scrollwest = 37; + builder_zoomin = 65530; + builder_zoomout = 65531; + builder_preferences = 116; + builder_savemap = 131155; + builder_savemapas = 0; + builder_savemapinto = 0; + builder_mapoptions = 113; + builder_reloadresources = 119; + builder_cancelmode = 27; + builder_gridsetup = 0; + builder_undo = 131162; + builder_redo = 131161; + builder_togglesnap = 0; + builder_clearselection = 67; + builder_toggleautomerge = 0; + builder_togglefullbrightness = 66; + builder_centerinscreen = 36; + builder_moveleft = 83; + builder_moveright = 70; + builder_moveforward = 69; + builder_movebackward = 68; + buildermodes_thingsmode = 84; + buildermodes_linedefsmode = 76; + buildermodes_verticesmode = 86; + buildermodes_sectorsmode = 83; + builder_insertvertex = 65622; + builder_testmap = 120; + builder_acceptmode = 13; + builder_classicedit = 2; + builder_classicselect = 1; + builder_deleteitem = 46; + buildermodes_finishdraw = 2; + buildermodes_drawpoint = 1; + buildermodes_drawlinesmode = 131140; + buildermodes_flipsidedefs = 65606; + buildermodes_fliplinedefs = 70; + buildermodes_joinsectors = 74; + buildermodes_removepoint = 8; + buildermodes_mergesectors = 65610; + buildermodes_splitlinedefs = 0; + buildermodes_visualmode = 87; + buildermodes_curvelinesmode = 65603; + builder_thingsfilterssetup = 0; + builder_insertitem = 45; + builder_griddec = 219; + builder_gridinc = 221; + buildermodes_findmode = 114; + buildermodes_makesectormode = 77; + buildermodes_editselectionmode = 69; + builder_copyselection = 131139; + builder_pasteselection = 131158; + builder_cutselection = 131160; + buildermodes_gradientbrightness = 71; + builder_categories = 0; + builder_selectgroup6 = 54; + builder_selectgroup1 = 49; + builder_selectgroup10 = 48; + builder_selectgroup7 = 55; + builder_selectgroup2 = 50; + builder_selectgroup5 = 53; + builder_selectgroup8 = 56; + builder_selectgroup9 = 57; + builder_selectgroup3 = 51; + builder_selectgroup4 = 52; + builder_assigngroup1 = 131121; + builder_assigngroup6 = 131126; + builder_assigngroup9 = 131129; + builder_assigngroup2 = 131122; + builder_assigngroup4 = 131124; + builder_assigngroup10 = 131120; + builder_assigngroup8 = 131128; + builder_assigngroup7 = 131127; + builder_assigngroup3 = 131123; + builder_assigngroup5 = 131125; + buildermodes_makedoor = 65604; + builder_pan_view = 32; + builder_openscripteditor = 121; + builder_testaction = 0; + builder_viewmodefloors = 0; + builder_viewmodenormal = 0; + builder_viewmodeceilings = 0; + buildermodes_errorcheckmode = 115; + builder_movedoublespeed = 16; + builder_visualselect = 1; + builder_visualedit = 2; + buildermodes_raisesector8 = 65530; + buildermodes_lowersector8 = 65531; + buildermodes_raisesector1 = 131066; + buildermodes_lowersector1 = 131067; + buildermodes_showvisualthings = 84; + buildermodes_movetextureright = 39; + buildermodes_movetextureleft = 37; + buildermodes_movetextureup = 38; + buildermodes_movetexturedown = 40; + buildermodes_textureselect = 131074; + buildermodes_texturepaste = 4; + buildermodes_texturecopy = 131139; + buildermodes_visualautoalign = 131137; + buildermodes_visualautoalignx = 65; + buildermodes_visualautoaligny = 65601; + buildermodes_togglemiddletexture = 77; + buildermodes_togglelowerunpegged = 76; + buildermodes_toggleupperunpegged = 85; + buildermodes_togglegravity = 71; + builder_insertpreviousprefab = 80; + builder_createprefab = 0; + builder_insertprefabfile = 131152; + buildermodes_placevisualstart = 131159; + builder_toggleinfopanel = 192; + buildermodes_resettexture = 65618; + builder_showerrors = 122; + buildermodes_pasteproperties = 196694; + buildermodes_floodfilltextures = 65540; + buildermodes_copyproperties = 196675; + buildermodes_texturepasteoffsets = 65622; + buildermodes_movetextureleft8 = 65573; + buildermodes_movetextureright8 = 65575; + buildermodes_lowerfloor8 = 196603; + buildermodes_selectdoublesided = 65586; + buildermodes_raisefloor8 = 196602; + buildermodes_movetextureup8 = 65574; + buildermodes_texturecopyoffsets = 65603; + buildermodes_gradientfloors = 131143; + buildermodes_selectsinglesided = 65585; + buildermodes_gradientceilings = 65607; + buildermodes_flipselectionh = 0; + buildermodes_raiseceiling8 = 131066; + buildermodes_lowerceiling8 = 131067; + buildermodes_flipselectionv = 0; + buildermodes_movetexturedown8 = 65576; + builder_pasteselectionspecial = 0; + buildermodes_togglehighlight = 72; + copypastesectorprops_copysectorprops = 0; + copypastesectorprops_pastesectorprops = 0; + imagedrawingexample_imageexamplemode = 0; + statistics_statisticsmode = 0; + builder_viewmodefloorcolor = 0; + builder_viewmodeceilingcolor = 0; + builder_viewmodethingcolor = 0; + buildermodes_togglelightonly = 86; + builder_thingrotateleft = 188; + builder_thingrotateright = 190; + copypastesectorprops_pastesectorcolors = 65617; + copypastesectorprops_copysectorcolors = 81; +} + + +colors +{ + color0 = -16777216; + color1 = -11425537; + color2 = -1; + color3 = -4259937; + color4 = -8355585; + color5 = -21504; + color6 = -65536; + color7 = -128; + color8 = -12171706; + color9 = -13018769; + color10 = -16711681; + color11 = -24576; + color12 = -49152; + color13 = -1; + color14 = -4144960; + color15 = -16777216; + color16 = -6908266; + color17 = -16753716; + color18 = -11366857; + color19 = -6671822; + color20 = -9868951; + color21 = -12490271; + color22 = -14513374; + color23 = -14634326; + color24 = -5103070; + color25 = -7077677; + color26 = -4684277; + color27 = -4144960; + color28 = -8355712; + color29 = -16728065; + color30 = -13447886; + color31 = -5247250; + color32 = -40121; + color33 = -1146130; + color34 = -256; + color35 = -657931; + color36 = -18751; + color37 = -29696; + color38 = -4343957; + color39 = -2448096; + color40 = -32704; + color41 = -16744384; + color42 = -65281; + color43 = -65536; +} \ No newline at end of file diff --git a/Build/Compilers/Doom64/blam.cfg b/Build/Compilers/Doom64/blam.cfg new file mode 100644 index 0000000..0642512 --- /dev/null +++ b/Build/Compilers/Doom64/blam.cfg @@ -0,0 +1,13 @@ + +compilers +{ + // This defines what files a compiler uses + // The setting named "program" defines what .exe to run + // The "interface" setting defines what interal interface to use for processing and error feedback + // All others are the required files (the setting names do not matter) + doom64_blam + { + interface = "BlamCompiler"; + program = "blam.exe"; + } +} diff --git a/Build/Compilers/Doom64/blam.exe b/Build/Compilers/Doom64/blam.exe new file mode 100644 index 0000000000000000000000000000000000000000..d674fdc111cd7d942bc7abbed187522e5565645a GIT binary patch literal 26624 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 lgHl0T-twkQ4$=z&6q1h6p4Pm9*PH$|hOi7W!s` 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

G#m=|41mKTAc#W|Sl+F z%qYp07B7dYA4b0US(Z_WiZ-POz!#Oh;=)+f`P9EaoQes~QZSggSw8h&@+=kr!Q-kQ zFXS{eO6hmLg$Y7KZ2IAVdk0J;c>Gb{M8g9deOl=AReaJCp^G3HRlSJT4imnJ5(G*l8)fpm40fiTpG%^XGJIR@Ec8P8FJ6ER$AZM{IW>Sh7XXR!&K&vPYElI!&;3+Xd* zfp`kfDiv%H%tTE!U7w0&c8F

@ob}XnvJi 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 + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/Build/Refmanual.chm b/Build/Refmanual.chm new file mode 100644 index 0000000000000000000000000000000000000000..3a74e0f6f24e08265ddf44eceb524bcc2060d130 GIT binary patch literal 193207 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%0D=3;a^-7tQ8Va319V&0}tncZ@Q_f`2pNr*) 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%0At=Xf@M5{YW*QVkMkVIqxfi#N)7roIT3`M{%(% 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 literal 0 HcmV?d00001 diff --git a/Build/Scintilla.dll b/Build/Scintilla.dll new file mode 100644 index 0000000000000000000000000000000000000000..c5c68798006af64b1280d8009e9b03d7637b9587 GIT binary patch literal 442368 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�xb2bUDqdVOa-)hD*Nm)IesRsn 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ℑ;w;qi*Tv)Pfaa9lQp|*% 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 Icne?P9~qn>jQ{`u literal 0 HcmV?d00001 diff --git a/Build/Scripting/Doom64_BLAM.cfg b/Build/Scripting/Doom64_BLAM.cfg new file mode 100644 index 0000000..ed11da7 --- /dev/null +++ b/Build/Scripting/Doom64_BLAM.cfg @@ -0,0 +1,195 @@ +/*******************************************************************\ + Doom Builder Script highlighting definitions for BLAM +\*******************************************************************/ + +// Compiler settings +compiler = "doom64_blam"; +parameters ="\"%FI\" \"%FO\" -i \"%PS\""; +resultlump = "MACROS"; + +// Editor settings +description = "Doom64 BLAM Script"; +codepage = 0; +extensions = "txt"; +casesensitive = false; +insertcase = 0; // 0=Normal, 1=Lowercase, 2=Uppercase +lexer = 35; +functionopen = "("; +functionclose = ")"; +argumentdelimiter = ","; +terminator = ";"; +keywordhelp = ""; + +keywords +{ + #define = "#Define identifier expression"; + #include = "#Include"; + #setdir = "Set directory to search for files to include"; + Delay = "Delay(tic)"; + Wait = "Waits for the current action to finish"; + Loop = "Loop(amount) { ... }"; + + Door_Open = "Door_Open(tag)"; + Door_Close = "Door_Close(tag)"; + Door_Raise = "Door_OpenWaitClose(tag)"; + Door_OWC = "Door_OpenWaitClose(tag)"; + Door_OpenWaitClose = "Door_OpenWaitClose(tag)"; + Door_CloseWaitOpen = "Door_CloseWaitLongOpen(tag)"; + Door_CloseWaitLongOpen = "Door_CloseWaitLongOpen(tag)"; + Door_RaiseFast = "Door_OpenWaitCloseFast(tag)"; + Door_OWCFast = "Door_OpenWaitCloseFast(tag)"; + Door_OpenWaitCloseFast = "Door_OpenWaitCloseFast(tag)"; + Door_OpenFast = "Door_OpenFast(tag)"; + Door_CloseFast = "Door_CloseFast(tag)"; + + Floor_Raise = "Floor_RaiseToCeiling(tag)"; + Floor_RaiseToCeiling = "Floor_RaiseToCeiling(tag)"; + Floor_Lower = "Floor_LowerToNearest(tag)"; + Floor_LowerToNearest = "Floor_LowerToNearest(tag)"; + Floor_LowerFast = "Floor_LowerToNearestFast(tag)"; + Floor_LowerToNearestFast = "Floor_LowerToNearestFast(tag)"; + Floor_LowerChange = "Floor_LowerToLowestChange(tag)"; + Floor_LowerToLowestChange = "Floor_LowerToLowestChange(tag)"; + Floor_LowerToLowest = "Floor_LowerToLowest(tag)"; + Floor_RaiseCrush = "Floor_RaiseCrush(tag)"; + Floor_RaiseTwentyFour = "Floor_RaiseTwentyFour(tag)"; + Floor_RaiseTwentyFourChange = "Floor_RaiseTwentyFourChange(tag)"; + Floor_RaiseToNearest = "Floor_RaiseToNearest(tag)"; + Floor_RaiseToNearestTwo = "Floor_RaiseToNearest(tag)"; + Floor_MoveByValue = "Floor_MoveByValue(tag, value)"; + Floor_MoveBy = "Floor_MoveByValue(tag, value)"; + Floor_MoveByValueFast = "Floor_MoveByValueFast(tag, value)"; + Floor_MoveByFast = "Floor_MoveByValueFast(tag, value)"; + Floor_MoveByValueInstant = "Floor_MoveByValueInstant(tag, value)"; + Floor_MoveByInstant = "Floor_MoveByValueInstant(tag, value)"; + Floor_SetHeight = "Floor_SetHeight(tag, height)"; + Floor_Set = "Floor_SetHeight(tag, height)"; + Floor_MoveByHeight = "Floor_MoveToHeight(tag, height)"; + Floor_MoveToHeight = "Floor_MoveToHeight(tag, height)"; + Floor_MoveTo = "Floor_MoveToHeight(tag, height)"; + + Stairs_Build = "Stairs_BuildEightSlow(tag)"; + Stairs_BuildEight = "Stairs_BuildEightSlow(tag)"; + Stairs_BuildSlow = "Stairs_BuildEightSlow(tag)"; + Stairs_BuildEightSlow = "Stairs_BuildEightSlow(tag)"; + Stairs_BuildSixteen = "Stairs_BuildSixteenFast(tag)"; + Stairs_BuildFast = "Stairs_BuildSixteenFast(tag)"; + Stairs_BuildSixteenFast = "Stairs_BuildSixteenFast(tag)"; + + Ceiling_RaiseCrush = "Ceiling_CrushFastLoop(tag)"; + Ceiling_CrushFastLoop = "Ceiling_CrushFastLoop(tag)"; + Ceiling_RaiseCrushOnce = "Ceiling_CrushSlowLoop(tag)"; + Ceiling_CrushSlowLoop = "Ceiling_CrushSlowLoop(tag)"; + Ceiling_LowerToFloor = "Ceiling_LowerToFloor(tag)"; + Ceiling_RaiseCrushTwo = "Ceiling_LowerToFloorTwo(tag)"; + Ceiling_LowerToFloorTwo = "Ceiling_LowerToFloorTwo(tag)"; + Ceiling_StopCrusher = "Ceiling_StopCrusher(tag)"; + Ceiling_SilentCrusher = "Ceiling_InstantKill(tag)"; + Ceiling_SilentCrush = "Ceiling_InstantKill(tag)"; + Ceiling_InstantKill = "Ceiling_InstantKill(tag)"; + Ceiling_MoveByValue = "Ceiling_MoveByValue(tag, value)"; + Ceiling_MoveBy = "Ceiling_MoveByValue(tag, value)"; + Ceiling_MoveByValueFast = "Ceiling_MoveByValueFast(tag, value)"; + Ceiling_MoveByFast = "Ceiling_MoveByValueFast(tag, value)"; + Ceiling_MoveByValueInstant = "Ceiling_MoveByValueInstant(tag, value)"; + Ceiling_MoveByInstant = "Ceiling_MoveByValueInstant(tag, value)"; + Ceiling_RaiseCrushOnceFast = "Ceiling_CrushRaiseFastOnce(tag)"; + Ceiling_CrushRaiseFastOnce = "Ceiling_CrushRaiseFastOnce(tag)"; + Ceiling_SetHeight = "Ceiling_SetHeight(tag, height)"; + Ceiling_Set = "Ceiling_SetHeight(tag, height)"; + Ceiling_MoveByHeight = "Ceiling_MoveToHeight(tag, height)"; + Ceiling_MoveToHeight = "Ceiling_MoveToHeight(tag, height)"; + Ceiling_MoveTo = "Ceiling_MoveToHeight(tag, height)"; + + Plat_DownWaitUp = "Plat_DownWaitUp(tag)"; + Plat_DWU = "Plat_DownWaitUp(tag)"; + Plat_RaiseChange = "Plat_RaiseChange(tag)"; + Plat_PerpetualRaise = "Plat_DownWaitUpLoop(tag)"; + Plat_Loop = "Plat_DownWaitUpLoop(tag)"; + Plat_DWULoop = "Plat_DownWaitUpLoop(tag)"; + Plat_DownWaitUpLoop = "Plat_DownWaitUpLoop(tag)"; + Plat_Stop = "Plat_Stop(tag)"; + Plat_RaiseByTwentyFourChange = "Plat_RaiseByTwentyFourChange(tag)"; + Plat_RaiseByThirtyTwoChange = "Plat_RaiseByThirtyTwoChange(tag)"; + Plat_DownWaitUpFast = "Plat_DownWaitUpFast(tag)"; + Plat_DWUFast = "Plat_DownWaitUpFast(tag)"; + Plat_UpWaitDown = "Plat_UpWaitDown(tag)"; + Plat_UWD = "Plat_UpWaitDown(tag)"; + Plat_UpWaitDownFast = "Plat_UpWaitDownFast(tag)"; + Plat_UWDFast = "Plat_UpWaitDownFast(tag)"; + Plat_DownUpByValue = "Plat_DownWaitUpByValue(tag, value)"; + Plat_DownWaitUpByValue = "Plat_DownWaitUpByValue(tag, value)"; + Plat_DownWaitUpBy = "Plat_DownWaitUpByValue(tag, value)"; + Plat_DWUBy = "Plat_DownWaitUpByValue(tag, value)"; + Plat_DownUpFastByValue = "Plat_DownWaitUpFastByValue(tag, value)"; + Plat_DownWaitUpFastByValue = "Plat_DownWaitUpFastByValue(tag, value)"; + Plat_DownWaitUpFastBy = "Plat_DownWaitUpFastByValue(tag, value)"; + Plat_DWUFastBy = "Plat_DownWaitUpFastByValue(tag, value)"; + Plat_UpDownByValue = "Plat_UpWaitDownByValue(tag, value)"; + Plat_UpWaitDownByValue = "Plat_UpWaitDownByValue(tag, value)"; + Plat_UpWaitDownBy = "Plat_UpWaitDownByValue(tag, value)"; + Plat_UWDBy = "Plat_UpWaitDownByValue(tag, value)"; + Plat_UpDownFastByValue = "Plat_UpWaitDownFastByValue(tag, value)"; + Plat_UpWaitDownFastByValue = "Plat_UpWaitDownFastByValue(tag, value)"; + Plat_UpWaitDownFastBy = "Plat_UpWaitDownFastByValue(tag, value)"; + Plat_UWDFastBy = "Plat_UpWaitDownFastByValue(tag, value)"; + + Teleport_ToDest = "Teleport_Normal(tid)"; + Teleport_Normal = "Teleport_Normal(tid)"; + Teleport_Stomp = "Teleport_Silent(tid)"; + Teleport_Silent = "Teleport_Silent(tid)"; + + Thing_ModifyFlags = "Thing_ModifyFlags(tid)"; + Thing_Alert = "Thing_Alert(tid)"; + Thing_SpawnDart = "Thing_SpawnDart(tid)"; + Thing_Spawn = "Thing_Spawn(tid)"; + Thing_SpawnTracer = "Thing_SpawnTracer(tid)"; + Thing_SetReactionTime = "Thing_SetReactionTime(tics)"; + Thing_Dissolve = "Thing_FadeOut(tid)"; + Thing_FadeOut = "Thing_FadeOut(tid)"; + + Sector_SetFloorColorID = "Sector_SetFloorColorID(tag, id)"; + Sector_SetCeilingColorID = "Sector_SetCeilingColorID(tag, id)"; + Sector_SetThingColorID = "Sector_SetThingColorID(tag, id)"; + Sector_SetUpperWallColorID = "Sector_SetUpperWallColorID(tag, id)"; + Sector_SetLowerWallColorID = "Sector_SetLowerWallColorID(tag, id)"; + Sector_CopyFlags = "Sector_CopyFlags(dsttag, srctag)"; + Sector_CopySpecials = "Sector_CopySpecials(dsttag, srctag)"; + Sector_CopyLights = "Sector_CopyLights(dsttag, srctag)"; + Sector_CopyTextures = "Sector_CopyTextures(dsttag, srctag)"; + Sector_CopyLightsAndInterpolate = "Sector_CopyLightsAndInterpolate(dsttag, srctag)"; + + Line_CopyFlags = "Line_CopyFlags(dsttag, srctag)"; + Line_CopyTextures = "Line_CopyTextures(dsttag, srctag)"; + Line_CopySpecials = "Line_CopySpecials(dsttag, srctag)"; + Line_TriggerRandomLinesByTag = "Line_TriggerRandomLinesByTag(tags)"; + + Elevator_MoveByValue = "Elevator_MoveByValue(tag, value)"; + Elevator_MoveBy = "Elevator_MoveByValue(tag, value)"; + + Pillar_OpenByValue = "Pillar_OpenByValue(tag, value)"; + Pillar_OpenBy = "Pillar_OpenByValue(tag, value)"; + + Camera_Clear = "Camera_Clear(tid)"; + Camera_Set = "Camera_Set(tid)"; + Camera_MoveAndAim = "Camera_MoveAndAim(tid, nexttid)"; + + Macro_Suspend = "Macro_Suspend(id)"; + Macro_Enable = "Macro_Enable(id)"; + Macro_Disable = "Macro_Disable(id)"; + + Player_Freeze = "Player_Freeze(tics)"; + Quake = "Quake(tics)"; + SetLightID = "SetLightID(dst, src)"; + Exit = "Exit"; + ExitToLevel = "ExitToLevel(map)"; + UnlockCheatMenu = "UnlockCheatMenu "; + DisplaySkyLogo = "DisplaySkyLogo"; + No_Op = "No_Op"; + +} + +constants +{ + +} diff --git a/Build/Sharpzip.dll b/Build/Sharpzip.dll new file mode 100644 index 0000000000000000000000000000000000000000..509d52144a3b3fa5aa431ab706b352e8cf019103 GIT binary patch literal 180224 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

    !s9? z37da-rCWjJ(TP~H%PX;mSx%mC+jqDUTf_pCcZ<2s%nnGyE3#|dvJKB<&jsN;ZZ~)*uw;L4Z4"F1&Ms)THQiMizGh1#juG&{kU%={OF|7|cfMfOt+ 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

    ~olNF?n#$z&F~N;Na}Kh)6mt8xxm) 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

    bv)C$ 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 z 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 z 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 |Z3$a9fOD5bn$PAJpUlk!Ny6yH_!V6sf+pG()H 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 z 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

    C+V9?hy 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%0A`?a(>wC-bW9actxh}Y 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%0AgDfFTRIIp1_2`Uwm&NhjlwviD4=3_(YWl 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

    NKR$2pYlxk~pUknW|I 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

    Cyjbx87Tk7y=TIjh?yHrdlU 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%0A0( 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 BuWA4Q literal 0 HcmV?d00001 diff --git a/Build/Sprites/Action.png b/Build/Sprites/Action.png new file mode 100644 index 0000000000000000000000000000000000000000..146bf95b60c91c4c621338525331f82f17d42112 GIT binary patch literal 471 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCijSl0AZa85pY67#JE_7#My5g&JNkFq9fFFuY1&V6d9Oz#v{QXIG#NP=d3- zBeIx*fm;xS8S7KxCjtdIN?apKobz*YQ}ap~zMYx2A1EdP5(~~xD=AMbN@WO0%*-p% z^K%VRC^ObGHe31pkt)zp9G)(YAr}70DGCgJvU5MrpZirl;_CnZJUsXJ|4*p+|4#ex z=I8Pf+w!b?;=g~iUHU)X_SX0RAJ4u_sQG&2&j0js-+%QvkH7ps=(x|}v4n$!IfLYf ze{UcEFF*IS`QuL8-~+RSAOG#R%(HoJ%529xr)_gpW+%>a+}1p&w$A>@P0^zZTjug| z^Uio1-e1;aTE4Yq@-j)WSLJ$o|LY%LdG!BznJ>Gv$ctRp8w)IhKmFf+P5sY#KmH%_ z@sqi}v3V4SOt4HmdqQwm_GzCT&lZ(2azokrK2OwKBV-?7>)^5#vOkWemL z=DnS{tQHTdUcL3$E}3#*&dR6%m;0Yk^*27j&}AZg>eh`Xj91*>efLh6=>>)xgQu&X J%Q~loCIA{U!2BR0kYI^x zM2T~LZft9*^L?vAy!# zf6cw7|KG<)eE$D&>ev78=f!`m|GsM0f8nU)|Jy^`|LC76_^Yq1+jigDbth+nGhbd7 zb9UVR1uy8?5kIwTJ|y-r@XZZIlJwe+Yxq^GZWq|^SETHEX{N&CpWbt+?XNv X(w&|>&vl|e|1x;G`njxgN@xNAeAbzE literal 0 HcmV?d00001 diff --git a/Build/Sprites/Light.png b/Build/Sprites/Light.png new file mode 100644 index 0000000000000000000000000000000000000000..01d2313f9e8b936eee8bf4991ace6c79c7cb5cd5 GIT binary patch literal 509 zcmVpF33NqRbVF}#ZDnqB004<9jRpV!0h&ofK~#90V>r6C@;?I%n3?J$ zI1ER3O-1nj{eJoX^sZ8f08Ep-tOSyJm^PS+|K1<|Ke00xp&~KL^S`a77D6B;KJY)P zmbZJsjMMw)|6e%0_CK;19DI6q^8cnKmH&f$jQ^Kpd7w(dROh9-|Cf~(!EOQwaBy(^ zXJ==}lpqLhpIh^P-=-=5(-Hzvbw9ec`TxI`z3~64xw-#sEOikI&K+Fv|M!cH z{~uqR_`f1Q;J=iF2tsn%oW}p>cIW?JG$r+au#XX@!$83X!Xm&km7Be>hE4$`yZ4%zP`Qo|Mk7S|F53e3Xb>e)FAv8+&;hN|Ccwn|Nncl?>{Jx zKHOXNf8V;I|C2iM{!eQ4`OnSGjcNeM39!iAvvJb@hc}M=e|CA||AGwv|DZJFVyE#x zAzWj)M|F#B=vR>$Ps#U6O+D*kpUzAt0s0NC#HO?XJ+|@zsF9uQ%8EvOJvlEwzj%x z3Uc2zn{b&$)%NT;51$LA~vJze6aJQKYQt0@xiCy7l4Wp!Du zEIRc*UXHj)Cqra^L~&S76Q|Mw<(<+SY<4Hlz@>DL|>r+qYu zRfIzxHnIt(UiIU#w``xmpPV(Q2zVO_KW@WntR~QB<;%i2{oW1^tAxTDi?*;8oB0se zY)7$MkI>nAoOjV7F0?fKB^14bt(+DGnsfsCS_N(9dX$w1OaBiQ`*%xqY6U`NMd@-W Z`~)lWMb|1G)Gq)4002ovPDHLkV1nv~2t@z@ literal 0 HcmV?d00001 diff --git a/Builder.sln b/Builder.sln new file mode 100644 index 0000000..eba6f3a --- /dev/null +++ b/Builder.sln @@ -0,0 +1,43 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.2.32616.157 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Builder", "Source\Core\Builder.csproj", "{818B3D10-F791-4C3F-9AF5-BB2D0079B63C}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BuilderModes", "Source\Plugins\BuilderModes\BuilderModes.csproj", "{B42D5AA0-F9A6-4234-9C4B-A05B11A64851}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Statistics", "Source\Plugins\Statistics\Statistics.csproj", "{FBC0A503-9152-4BE2-9B5C-128FFD0B0D3F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CopyPasteSectorProperties", "Source\Plugins\CopyPasteSectorProps\CopyPasteSectorProperties.csproj", "{A5F93B70-18D9-4F3C-9B72-BC8B5B13998E}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x86 = Debug|x86 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {818B3D10-F791-4C3F-9AF5-BB2D0079B63C}.Debug|x86.ActiveCfg = Debug|x86 + {818B3D10-F791-4C3F-9AF5-BB2D0079B63C}.Debug|x86.Build.0 = Debug|x86 + {818B3D10-F791-4C3F-9AF5-BB2D0079B63C}.Release|x86.ActiveCfg = Release|x86 + {818B3D10-F791-4C3F-9AF5-BB2D0079B63C}.Release|x86.Build.0 = Release|x86 + {B42D5AA0-F9A6-4234-9C4B-A05B11A64851}.Debug|x86.ActiveCfg = Debug|x86 + {B42D5AA0-F9A6-4234-9C4B-A05B11A64851}.Debug|x86.Build.0 = Debug|x86 + {B42D5AA0-F9A6-4234-9C4B-A05B11A64851}.Release|x86.ActiveCfg = Release|x86 + {B42D5AA0-F9A6-4234-9C4B-A05B11A64851}.Release|x86.Build.0 = Release|x86 + {FBC0A503-9152-4BE2-9B5C-128FFD0B0D3F}.Debug|x86.ActiveCfg = Debug|x86 + {FBC0A503-9152-4BE2-9B5C-128FFD0B0D3F}.Debug|x86.Build.0 = Debug|x86 + {FBC0A503-9152-4BE2-9B5C-128FFD0B0D3F}.Release|x86.ActiveCfg = Release|x86 + {FBC0A503-9152-4BE2-9B5C-128FFD0B0D3F}.Release|x86.Build.0 = Release|x86 + {A5F93B70-18D9-4F3C-9B72-BC8B5B13998E}.Debug|x86.ActiveCfg = Debug|x86 + {A5F93B70-18D9-4F3C-9B72-BC8B5B13998E}.Debug|x86.Build.0 = Debug|x86 + {A5F93B70-18D9-4F3C-9B72-BC8B5B13998E}.Release|x86.ActiveCfg = Release|x86 + {A5F93B70-18D9-4F3C-9B72-BC8B5B13998E}.Release|x86.Build.0 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {BD48AB84-D7E8-4EF4-BBB2-D2A8CA37FC49} + EndGlobalSection +EndGlobal diff --git a/Builder.suo b/Builder.suo new file mode 100644 index 0000000000000000000000000000000000000000..67cd261e44b5c0cbdaca824b28a8aa9b8509a24d GIT binary patch literal 100864 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 literal 0 HcmV?d00001 diff --git a/Help/Contents.hhc b/Help/Contents.hhc new file mode 100644 index 0000000..dc8a976 --- /dev/null +++ b/Help/Contents.hhc @@ -0,0 +1,212 @@ + + + + + + + + + +

    + diff --git a/Help/Excluded/actions.html b/Help/Excluded/actions.html new file mode 100644 index 0000000..1f8bd6b --- /dev/null +++ b/Help/Excluded/actions.html @@ -0,0 +1,23 @@ + + + + + About Actions + + + + + + + + + + +

    About Actions

    + +
    +

    + Every possible user input in Doom Builder is turned in an "action". You can change or assign new controls to these actions in the Preferences Dialog on the Controls tab. Some of these actions are available in all editing modes, which other actions are only available in one or more editing modes. Therefore, you can often re-use the same keys for multiple actions when they are in different editing modes. All the available actions that come with Doom Builder are explained in this part of the Reference Manual. +

    +
    + diff --git a/Help/Index.hhk b/Help/Index.hhk new file mode 100644 index 0000000..ddf4319 --- /dev/null +++ b/Help/Index.hhk @@ -0,0 +1,12 @@ + + + + + + + + + +
      +
    + diff --git a/Help/Refmanual.hhp b/Help/Refmanual.hhp new file mode 100644 index 0000000..4703695 --- /dev/null +++ b/Help/Refmanual.hhp @@ -0,0 +1,31 @@ +[OPTIONS] +Auto Index=Yes +Compatibility=1.1 or later +Compiled file=..\Build\Refmanual.chm +Contents file=Contents.hhc +Default Font=Verdana,10,0 +Default Window=default +Default topic=introduction.html +Display compile progress=Yes +Full-text search=Yes +Index file=Index.hhk +Language=0x409 English (United States) +Title=Doom Builder Reference Manual + +[WINDOWS] +default="Doom Builder Reference Manual","Contents.hhc","Index.hhk","introduction.html","introduction.html",,,,,0x2520,300,0x3806,[150,110,1100,800],0x108b0000,,,,,,0 + + +[FILES] +introduction.html +header.gif +editingmodes.html +userinterface.html +configurations.html +w_mainwindow.png +w_scripteditor.png +classiccontrols.html +scripts.js + +[INFOTYPES] + diff --git a/Help/a_template.html b/Help/a_template.html new file mode 100644 index 0000000..f31c190 --- /dev/null +++ b/Help/a_template.html @@ -0,0 +1,27 @@ + + + + + ACTIONTITLE Action + + + + + + + + + + +

    ACTIONTITLE Action

    + +
    +

    +

    Summary

    + Category:   ACTIONCATEGORY
    +
    + ACTIONDESCRIPTION + +

    +
    + diff --git a/Help/classiccontrols.html b/Help/classiccontrols.html new file mode 100644 index 0000000..3bb7311 --- /dev/null +++ b/Help/classiccontrols.html @@ -0,0 +1,49 @@ + + + + + Classic Controls + + + + + +
    +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    EStarts the Edit Selection Mode to move, resize, flip and/or rotate the highlighted or selected elements.
    LMBClick to select or deselect. Hold and drag to make a rectangular selection. While making a rectangular selection you can hold Shift to change the additive behavior.
    RMBClick once on an element or selection of elements to edit properties. Hold and drag to move the element or selection around. While dragging you can hold Shift to change the Snap-To-Grid behavior, and/or hold Control to change the Snap-To-Geometry behavior.
    CClears the selection.
    Control+CCopy the current selection or highlighted element to the clipboard.
    Control+XCuts the current selection or highlighted element and places it on the clipboard.
    Control+VPastes from the clipboard as a new selection in Edit Selection Mode.
    Control+WPlace or move the Visual Mode camera to the mouse coordinates. Next time you go into Visual Mode, you will be at these coordinates.
    +

    +
    + diff --git a/Help/compilerconfigs.html b/Help/compilerconfigs.html new file mode 100644 index 0000000..7330d56 --- /dev/null +++ b/Help/compilerconfigs.html @@ -0,0 +1,24 @@ + + + + + About Compiler Configurations + + + + + + + + + + + +

    About Compiler Configurations

    + +
    +

    + B +

    +
    + diff --git a/Help/configstructure.html b/Help/configstructure.html new file mode 100644 index 0000000..3c0e734 --- /dev/null +++ b/Help/configstructure.html @@ -0,0 +1,262 @@ + + + + + Configuration Syntax + + + + + + + + + + + + +

    Configuration Syntax

    + +
    +

    + All configurations used with Doom Builder follow a specific, structured syntax. The configurations are all in plain text format and can be edited with any plain text editor. Every setting in a configuration is in the following form: +

    +
    +settingname = value;
    +
    +

    Here are a few examples:

    +
    +doublesidedflag = 4;
    +defaulttexturescale = 1.0f;
    +scaledtextureoffsets = true;
    +gamename = "Doom";
    +
    +

    + There are a few rules to the setting names and values: +

      +
    • The setting name may not contain any spaces, tabs, newlines, dots or (back)slashes. +
    • The setting name must be unique within the configuration, or within the structure it is in. +
    • A decimal value must contain a dot and must end with 'f' +
    • Boolean settings may use the keywords true and false. +
    • Strings (texts) must begin with a doublequote (") and end with a doublequote. To include a doublequote in the string, prefix it with a backslash (\"). To use a backslash in a string, also prefix it with a backslash. +
    + Some settings do not require a value and their precense or absense alone is enough. Such a case would look like this: +

    +
    +enablelighting;
    +
    +

    + C-style comments can be inserted by using double slashes (//) for singleline comments and /* and */ for block comments. These comments will be completely ignored by Doom Builder. You can hide writings about your wildest dreams in configuration files! +

    + + + +

    Structures

    +

    + Configurations can also contain structures. Think of them as a collection of settings that have their own name space. The structure begins with a name and opens with an opening bracket ({). At the end of the structure it closes with a closing bracktet (}). It is common to ident the settings inside the structure with a single tab to make it easier to see that those settings belong in a structure. An example of a structure with settings: +

    +
    +winningnumbers
    +{
    +	car = 55;
    +	washmachine = 40;
    +	tools = 30;
    +}
    +
    +

    + Structures are often used by Doom Builder in cases where multiple collections of settings can be found. The name of a structure can also be just a number, so that a structure can describe information about a specific index number. Here is an example of such a case: +

    +
    +things
    +{
    +	1
    +	{
    +		name = "Player 1 start";
    +		color = "green";
    +	}
    +	
    +	2
    +	{
    +		name = "Player 2 start";
    +		color = "green";
    +	}
    +	
    +	138
    +	{
    +		name = "Hideous monster";
    +		color = "red";
    +	}
    +}
    +
    + Please note that in some structures, the order of the settings and structures is important. + + + +

    Including

    +

    + Some configurations (such as the Game Configurations) can become very large and complex, while some share the same values in many settings. For example, Eternity is a game engine that inherits features from Doom and Boom and adds on top of that. This is where including pieces from other configuration files becomes interesting. With the include function, we can insert features from Doom, then insert (and possible override) features from Boom and then add the Eternity ones. This saves us rewriting all the Doom and Boom features that already exists in other configurations.
    +
    + The include function takes two arguments. The first (mandatory) is the filename (path relative to the current file) and the second (optional) is the name of the structure to include. If the second argument is not given, the entire file will be included. Here is an example that shows how such an include functions look like: +

    +
    +include("commonsettings.cfg");
    +include("extras.cfg", "skills");
    +
    +

    + Below are a few examples that show what the results are when including settings that override settings with the same names among other things. For these examples we will be including a file named "extras.cfg", which contains the following settings: +

    +
    +maxtexturenamelength = 8;
    +skyflatname = "F_SKY1";
    +
    +options
    +{
    +	1 = "Low";
    +	2 = "Medium";
    +	3 = "High";
    +}
    +
    +

    + This example shows how the include function includes an entire file. This is the most basic include: +

    +
    +thingflags
    +{
    +	1 = "Easy";
    +	2 = "Medium";
    +	4 = "Hard";
    +}
    +
    +include("extras.cfg");
    +
    +

    Result:

    +
    +thingflags
    +{
    +	1 = "Easy";
    +	2 = "Medium";
    +	4 = "Hard";
    +}
    +
    +maxtexturenamelength = 8;
    +skyflatname = "F_SKY1";
    +
    +options
    +{
    +	1 = "Low";
    +	2 = "Medium";
    +	3 = "High";
    +}
    +
    +

    + The following example shows how a single structure from extras.cfg is included. Notice how only the contents of the structure are included and not the structure container itsself! +

    +
    +thingflags
    +{
    +	1 = "Easy";
    +	2 = "Medium";
    +	4 = "Hard";
    +}
    +
    +include("extras.cfg", "options");
    +
    +

    Result:

    +
    +thingflags
    +{
    +	1 = "Easy";
    +	2 = "Medium";
    +	4 = "Hard";
    +}
    +
    +1 = "Low";
    +2 = "Medium";
    +3 = "High";
    +
    +

    + If we want our included settings in a structure, we have to put the include function in the structure where we want our settings included. See the following example: +

    +
    +thingflags
    +{
    +	1 = "Easy";
    +	2 = "Medium";
    +	4 = "Hard";
    +}
    +
    +options
    +{
    +	0 = "None";
    +	
    +	include("extras.cfg", "options");
    +	
    +	4 = "Ultra";
    +}
    +
    +

    Result:

    +
    +thingflags
    +{
    +	1 = "Easy";
    +	2 = "Medium";
    +	4 = "Hard";
    +}
    +
    +options
    +{
    +	0 = "None";
    +	
    +	1 = "Low";
    +	2 = "Medium";
    +	3 = "High";
    +	
    +	4 = "Ultra";
    +}
    +
    +

    + The following example demonstrates how values can be overridden by using the same same setting name. It also shows that this does NOT change the order of the items. The first time an item is defined (either by including or because it is written) is where its position will be. See this example: +

    +
    +thingflags
    +{
    +	1 = "Easy";
    +	2 = "Medium";
    +	4 = "Hard";
    +}
    +
    +options
    +{
    +	0 = "None";
    +	
    +	include("extras.cfg", "options");
    +	
    +	2 = "Average";
    +	4 = "Ultra";
    +}
    +
    +

    Result:

    +
    +thingflags
    +{
    +	1 = "Easy";
    +	2 = "Medium";
    +	4 = "Hard";
    +}
    +
    +options
    +{
    +	0 = "None";
    +	
    +	1 = "Low";
    +	2 = "Average";
    +	3 = "High";
    +	
    +	4 = "Ultra";
    +}
    +
    +

    + Notice how the definition of setting "2" ("Average") does not move the already defined "2" ("Medium"), but instead only changes its value to "Average". +

    +
    + diff --git a/Help/configurations.html b/Help/configurations.html new file mode 100644 index 0000000..f3fca68 --- /dev/null +++ b/Help/configurations.html @@ -0,0 +1,29 @@ + + + + + About Configurations + + + + + + + + + + +

    About Configurations

    + +
    +

    + Doom Builder is a very flexible editor that can be customized for your own sourceport or mapping project. Most of the settings that are different among the sourceports are in configuration files. You can find these configurations in the subdirectories of your Doom Builder program directory. This part of the Reference Manual helps you writing your own configurations, but is only recommended for advanced users. +

    +

    + You can safely create your own configurations for your mapping project or sourceport. If there are any errors in your configuration, Doom Builder will show these in the Errors & Warnings dialog. Doom Builder is installed with several configurations which can be great examples to see how these things work. It is recommended to always create new configurations in separate files. Do not modify the files that are installed by Doom Builder, as these will be overwritten when a new version is installed. +

    +

    + When you made changes to the configurations in the Doom Builder subdirectories, you must restart Doom Builder to reload the configurations with your changes. +

    +
    + diff --git a/Help/default.css b/Help/default.css new file mode 100644 index 0000000..2d99fe6 --- /dev/null +++ b/Help/default.css @@ -0,0 +1,149 @@ +body +{ + background: #ffffff; + margin: 0 auto; + font: 76% Verdana,Tahoma,Arial,sans-serif; + text-align: left; + padding: 0 0 0 0; +} + +#title +{ + background: #f4f4f4 url(header.gif) bottom right no-repeat; + color: #515151; + clear: both; + margin: 0 0 0 0; + padding: 0; + height: 47px; + border-bottom-style: solid; + border-bottom-color: #ffffff; + border-bottom-width: 2px; +} + +#contents +{ + background: #ffffff; + padding: 0 10px 10px 10px; + line-height: 1.6em; + border-top-style: solid; + border-top-color: #f4f4f4; + border-top-width: 2px; +} + +#iframecontents +{ + background: #ffffff; + padding: 0 0 0 0; + line-height: 1.6em; + border-top-style: none; +} + +pre +{ + background: #f4f4f4; + color: #515151; + width: 400px; + margin: 10px 0px 10px 20px; + font: 100% Courier New,Courier,sans-serif; + text-align: left; + padding: 5px 5px 5px 10px; + border-style: solid; + border-color: #E0E0E0; + border-width: 1px; +} + +h1 +{ + padding: 10px 10px 10px 10px; + font-size: 1.8em; + margin: 0 0 0.5em 0; +} + +h2 +{ + color: #515151; + font-size: 1.5em; + margin: 1.5em 0 0.2em; +} + +h3 +{ + color: #515151; + font-size: 1.1em; + margin: 1.0em 0 0.1em; +} + +img +{ + padding: 1px; + display:inline; + background: #FFFFFF; + border: 0; +} + +a +{ + text-decoration: none; + color: #286ea0; +} + +a:hover +{ + text-decoration: underline; + color: #286ea0; +} + +a img +{ + border: 0; +} + +.left +{ + margin: 10px 10px 5px 0; + float: left; +} + +.right +{ + margin: 10px 0 5px 10px; + float: right; +} + +.textright +{ + text-align: right; +} + +.center +{ + text-align: center; +} + +.small +{ + font-size: 0.8em; +} + +.bold +{ + font-weight: bold; +} + +.grey +{ + color: #8C8C8C; +} + +.greycenter +{ + color: #8C8C8C; + text-align: center; +} + +.fat +{ + color: #515151; + font-size: 1.1em; +} + diff --git a/Help/e_brightness.html b/Help/e_brightness.html new file mode 100644 index 0000000..0b1b4d5 --- /dev/null +++ b/Help/e_brightness.html @@ -0,0 +1,26 @@ + + + + + Brightness Mode + + + + + + + + + + + +

    Brightness Mode

    + +
    +

    + With this mode you can easily change the brightness levels in your map. Make a selection of sectors with LMB. Then hold RMB and drag up or down to increase or decrease the brightness levels of the selected sectors. The brightness changes to the nearest supported brightness level (according to the chosen game configuration), but you can hold Shift to override this behavior.
    +
    + The order in which you select the sectors is important to the Make Brightness Gradient feature as it creates a gradient from the first selected sector to the last selected sector (the first and last are not modified). When you select the sectors, the order will be displayed with numbers in the sectors.
    +

    +
    + diff --git a/Help/e_curvelinedefs.html b/Help/e_curvelinedefs.html new file mode 100644 index 0000000..3dadb93 --- /dev/null +++ b/Help/e_curvelinedefs.html @@ -0,0 +1,38 @@ + + + + + Curve Linedefs Mode + + + + + + + + + + + +

    Curve Linedefs Mode

    + +
    +

    + This mode shows a small dialog window that you can use to create a curve from a linedef or multiple linedefs. The linedefs will be split in a number of smaller linedefs that make up the curve. Although this mode shows a dialog window, you can still use the main interface and zoom/move your view around the map.
    +
    + The curve linedefs editing mode is one of the classic (2D) editing modes. This mode is volatile, which means that this mode returns to the previous stable mode when the map is saved or closed, either accepting or discarding your preview changes. You can access this mode through the Linedefs Mode. + +

    Default Controls

    + + + + + + + + + +
    EnterAccept and apply the changes, and return to the previous mode.
    EscapeDiscard the changes and return to the previous mode.
    +

    +
    + diff --git a/Help/e_drawgeometry.html b/Help/e_drawgeometry.html new file mode 100644 index 0000000..c154fcd --- /dev/null +++ b/Help/e_drawgeometry.html @@ -0,0 +1,58 @@ + + + + + Draw Geometry Mode + + + + + + + + + + + + + +

    Draw Geometry Mode

    + +
    +

    + This editing mode allows you to draw your geometry. Draw lines by clicking with the left mouse button (LMB) where you want your vertices. Depending on the toolbar buttons, the vertex you are drawing next will snap to the grid and/or to nearby geometry. You can change this behavior by holding down Shift and/or Control. Your drawing ends when you draw onto the first drawn vertex (making a closed polygon) or when you press your right mouse button (RMB).
    +
    + Contrary to drawing in older editors, it is not required to draw in any specific way, such as drawing clockwise or drawing a complete polygon. You also do not need to traverse your existing lines when drawing a sector adjacent to existing geometry. The editor will solve any lines your drawing crosses, split sector you draw across and creates new sectors where needed.
    +
    + This editing mode is one of the classic (2D) editing modes. This mode is volatile, which means that this mode returns to the previous stable mode when the map is saved or closed, either accepting or discarding your preview changes. You can access this mode by pressing Control+D in any classic mode. + +

    Default Controls

    + + + + + + + + + + + + + + + + + + + + + + + + + +
    Control+DStarts this drawing mode (available from any classic mode).
    EnterAccept and apply the changes, and return to the previous mode.
    EscapeDiscard the changes and return to the previous mode.
    BackspaceRemoves the last drawn vertex.
    LMBDraws a new line by placing a vertex at the mouse cursor. Use in combination with Shift and/or Control to change the snapping behavior.
    LMBEnds and applies the drawing, and returns to the previous editing mode.
    +

    +
    + diff --git a/Help/e_editselection.html b/Help/e_editselection.html new file mode 100644 index 0000000..b8dc5fb --- /dev/null +++ b/Help/e_editselection.html @@ -0,0 +1,52 @@ + + + + + Edit Selection Mode + + + + + + + + + + + + + + + +

    Edit Selection Mode

    + +
    +

    + With this editing mode you can perform some more advanced operations on a selection of geometry or things. This editing mode is one of the classic (2D) editing modes. This mode is volatile, which means that this mode returns to the previous stable mode when the map is saved or closed, either accepting or discarding your preview changes. You can access this mode by making a selection or highlight and pressing E in most classic modes. Clicking on the map outside the selection will return to the previous mode, applying your changes. + +

    Functions

    +

    Drag selection

    + You can drag the selection by holding LMB on the selection rectangle to grab it and move it around. When grabbing the selection, the nearby vertex is highlighted and will be used to snap to other geometry while dragging. +

    Flip Horizontally

    + Accessible from the toolbar or by shortcut key, you can flip the entire selection horizontally. It is recommended to do this before rotating the selection. +

    Flip Vertically

    + Accessible from the toolbar or by shortcut key, you can flip the entire selection vertically. It is recommended to do this before rotating the selection. +

    Resize

    + Grab one of the handles on the edges of the selection rectangle using LMB and drag it to resize the selection. You can drag your mouse away from the selection and a ruler will appear that allows you to align the grabbed edge of the selection with existing geometry. +

    Rotate

    + Grab one of the handles on the corners of the selection rectangle using LMB and drag it to rotate the selection. The selection will normally only rotate by the nearest 45 degrees. You can hold Shift to rotate it freely. + +

    Default Controls

    + + + + + + + + + +
    EnterAccept and apply the changes, and return to the previous mode.
    EscapeDiscard the changes and return to the previous mode.
    +

    +
    + diff --git a/Help/e_findreplace.html b/Help/e_findreplace.html new file mode 100644 index 0000000..4de7a31 --- /dev/null +++ b/Help/e_findreplace.html @@ -0,0 +1,26 @@ + + + + + Find & Replace Mode + + + + + + + + + + + +

    Find & Replace Mode

    + +
    +

    + Looking for something? This is the mode you want to use to quickly find specific elements in your map. This mode is accessible from any classic mode using F3 and shows a dialog window that you can use to find items. Select the type of search you want to perform at the top of the dialog window. Then enter the value you are looking for. Depending on the type of search, you can click the browse button to select from a browser. Check the Replace checkbox if you want to replace the value of the found elements with another value. Click the Find / Replace button to perform the search.
    +
    + The results will be displayed in a list that opens. Click on one of the results to zoom in on that particular element. You can also focus the main window and scroll or zoom the map to see the selected elements. Click the Edit Selection button to edit the selected elements. +

    +
    + diff --git a/Help/e_linedefs.html b/Help/e_linedefs.html new file mode 100644 index 0000000..166ad9d --- /dev/null +++ b/Help/e_linedefs.html @@ -0,0 +1,68 @@ + + + + + Linedefs Mode + + + + + + + + + + + + +

    Linedefs Mode

    + +
    +

    + With this mode you can edit the linedefs in your map. Every linedef has a front (right) and back (left) side which make up the boundaries of your sectors. You can edit the sidedef properties through the linedefs in this mode. Drag the linedefs to move them and snap them to the grid and other geometry. Hold Shift and/or Control to disable snapping to the grid and geometry.
    +
    + The linedefs editing mode is one of the classic (2D) editing modes. + +

    Default Controls

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    LSwitches from any other classic editing mode to this mode.
    InsertStarts the Draw Geometry Mode.
    DeleteDeletes the selected linedefs. Note that this may break your sectors.
    FFlips the linedef and changes the sidedefs so that they remain in their correct position.
    Shift+FFlips the sidedefs to the other side of the linedef.
    Shift+SSplits the linedef with a new vertex on the linedef, nearest to your mouse position.
    Shift+1Keeps only the single-sided linedefs selected.
    Shift+2Keeps only the double-sided linedefs selected.
    Shift+CStarts the Curve Linedefs Mode to make curves with the selected or highlighted linedefs.
    + +

    +
    + diff --git a/Help/e_makesectors.html b/Help/e_makesectors.html new file mode 100644 index 0000000..6521263 --- /dev/null +++ b/Help/e_makesectors.html @@ -0,0 +1,42 @@ + + + + + Make Sectors Mode + + + + + + + + + + + +

    Make Sectors Mode

    + +
    +

    + With this mode you can create new sectors from existing geometry only. This is useful to fix broken sectors, split the islands from a single sector into multiple sectors, or create sectors from enclosed void areas. When moving the mouse over the map, the potential sector is highlighted. Use LMB to create the new sector.
    +
    + The Make Sectors editing mode is one of the classic (2D) editing modes. + +

    Default Controls

    + + + + + + + + + + + + + +
    MSwitches from any other classic editing mode to this mode.
    LMBClick to create a new, closed sector.
    RMBClick to create a new, closed sector and edit its properties.
    +

    +
    + diff --git a/Help/e_mapanalysis.html b/Help/e_mapanalysis.html new file mode 100644 index 0000000..b6f067f --- /dev/null +++ b/Help/e_mapanalysis.html @@ -0,0 +1,25 @@ + + + + + Map Analysis Mode + + + + + + + + + + + + +

    Map Analysis Mode

    + +
    +

    + This mode helps finding problems in your map. It shows a small dialog window with the checks you wish to perform. Press F4 in any classic mode to switch to this mode. Select the checks to perform and click the Start Analysis button. The problems that are found are displayed in a list. Click on of the problems to zoom in on the subject and show a description of the problem below the list. For some problems, buttons appear below the description that you can use to quickly fix the problem. +

    +
    + diff --git a/Help/e_sectors.html b/Help/e_sectors.html new file mode 100644 index 0000000..90d40c6 --- /dev/null +++ b/Help/e_sectors.html @@ -0,0 +1,88 @@ + + + + + Sectors Mode + + + + + + + + + + + + + + +

    Sectors Mode

    + +
    +

    + In this mode you can edit the sectors in your map. Every sector is formed by the sidedefs around the sector (which are the sides of the linedefs). Drag the sectors to move them and snap them to the grid and other geometry. Hold Shift and/or Control to disable snapping to the grid and geometry. You can also create a stairs or gradient brightness by using the buttons on the toolbar.
    +
    + The order in which you select the sectors is important to some of the functions of the editing mode and, as long as you stay in an editing mode that keeps sectors selected, the order will stay the same. When you select the sectors, the order will be displayed with numbers in the sectors.
    +
    + The sectors editing mode is one of the classic (2D) editing modes. + +

    Default Controls

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    SSwitches from any other classic editing mode to this mode.
    InsertStarts the Draw Geometry Mode.
    DeleteDeletes the selected sectors and associated sidedefs. When deleting a sector that is enclosed inside a larger sector, this will create a hole (or 'pillar') inside that sector. If you wish to remove the sector without creating a void area, use the Linedefs Mode to delete the lines instead.
    JJoins to selected sector to become one sector. The properties of the first selected sector will be retained.
    Shift+JMerge the selected sectors to become one sector. This is the same as joining, with the addition that lines in between the selected sectors are automatically removed.
    Shift+DCreate a door from the selected or highlighted sectors. This will show a dialog which allows you to choose textures and flats for the door.
    GMake gradient brightness levels from first selected sector to last selected sector.
    Shift+ScrollDownLowers the ceiling of the selected or highlighted sectors by 8 mappixels.
    Shift+ScrollUpRaises the ceiling of the selected or highlighted sectors by 8 mappixels.
    Shift+GMake gradient ceiling heights from first selected sector to last selected sector.
    Control+ScrollDownLowers the floors of the selected or highlighted sectors by 8 mappixels.
    Control+ScrollUpRaises the floors of the selected or highlighted sectors by 8 mappixels.
    Control+GMake gradient floor heights (stairs) from first selected sector to last selected sector.
    + +

    +
    + diff --git a/Help/e_things.html b/Help/e_things.html new file mode 100644 index 0000000..91a9a82 --- /dev/null +++ b/Help/e_things.html @@ -0,0 +1,43 @@ + + + + + Things Mode + + + + + + + + + + + +

    Things Mode

    + +
    +

    + With this editing mode you edit the things in your map. Drag the things to move them and snap them to the grid. Hold Shift to disable snapping to the grid. You can use the Things Filter in the toolbar to show only things with specific properties (such as multiplayer things or things that only appear at a hard skill level).
    +
    + The things editing mode is one of the classic (2D) editing modes. + +

    Default Controls

    + + + + + + + + + + + + + +
    TSwitches from any other classic editing mode to this mode.
    InsertInserts a new thing.
    DeleteDeletes the selected or highlighted things.
    + +

    +
    + diff --git a/Help/e_vertices.html b/Help/e_vertices.html new file mode 100644 index 0000000..c6e87b6 --- /dev/null +++ b/Help/e_vertices.html @@ -0,0 +1,43 @@ + + + + + Vertices Mode + + + + + + + + + + + +

    Vertices Mode

    + +
    +

    + This editing mode allows you to edit the vertices in your map. You can insert new vertices that automatically split linedefs and deleting a vertex that only has two linedefs attached will reconnect the linedefs properly. You can drag the vertices to move them and snap them to the grid and geometry. Hold Shift and/or Control to disable snapping to the grid and geometry.
    +
    + The vertices editing mode is one of the classic (2D) editing modes. + +

    Default Controls

    + + + + + + + + + + + + + +
    VSwitches from any other classic editing mode to this mode.
    InsertInserts a new vertex at the mouse coordinates. When near a linedef, the linedef will be split with this vertex.
    DeleteDeletes the selected vertices. Note that this may also remove attached linedefs and break your sectors.
    + +

    +
    + diff --git a/Help/e_visual.html b/Help/e_visual.html new file mode 100644 index 0000000..6a3d133 --- /dev/null +++ b/Help/e_visual.html @@ -0,0 +1,181 @@ + + + + + Visual Mode + + + + + + + + + + + + +

    Visual Mode

    + +
    +

    + The Visual Mode is different from the other modes. In this mode you can walk and fly through your map in 3D and see it as it would look like in game. With the crosshair you can aim at objects (floors, ceilings, walls and things) and edit them instantly. You can also select objects and perform actions on all objects together. Note that not all actions apply to an entire selection. + +

    Default Controls

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    WSwitches from any classic editing mode to this mode and back to the previous mode.
    LMBSelect the targeted object. For walls, hold down this button to drag the texture offsets.
    RMBEdit the targeted object's properties.
    MMBPastes the copied texture image or flat image onto the target or selection.
    Control+RMBOpens the Image Browser to select a new texture or flat for the targeted or selected object.
    Shift+MMBFlood-fills with the copied texture image or flat image onto the target. This fills all adjacent textures or flats that are identical to the original. Note that this action is only applied to the targeted object and does not work for a selection.
    CClears the selection.
    EMove forward. Hold Shift to move faster.
    DMove backward. Hold Shift to move faster.
    SStrafe left. Hold Shift to move faster.
    FStrafe right. Hold Shift to move faster.
    GToggle gravity on/off.
    BToggle full-brightness on/off.
    LToggle lower unpegged linedef flag on/off.
    UToggle upper unpegged linedef flag on/off.
    TToggle things on/off/boxed. When things are set to boxed, you will see the bounding box of things around them in the color of their category.
    AAuto-aligns the neighbouring textures horizontally, until a wall is encountered that has different textures. Note that this action is only applied to the targeted object and does not work for a selection.
    Shift+AAuto-aligns the neighbouring textures vertically, until a wall is encountered that has different textures. The vertical alignment takes the ceiling height differences between sidedefs into account. Note that this action is only applied to the targeted object and does not work for a selection.
    Shift+CCopies the targeted texture offsets for pasting. Note that this works only for the targeted object, the selection is ignored.
    Control+CCopies the targeted texture image or flat image for pasting. Note that this works only for the targeted object, the selection is ignored.
    Control+Shift+CCopies the targeted object properties for pasting. Note that this works only for the targeted object, the selection is ignored.
    Shift+VPastes the copied texture offsets onto the target or selection.
    Control+Shift+VPastes the copied object properties onto the target or selection.
    ScrollUpRaises the targeted or selected object by 8 mappixels.
    ScrollDownLowers the targeted or selected object by 8 mappixels.
    Shift+ScrollUpRaises the targeted or selected object by 1 mappixel.
    Shift+ScrollDownLowers the targeted or selected object by 1 mappixel.
    Control+ScrollUpIncreases the brightness level of the targeted or selected object by 8.
    Control+ScrollDownDecreases the brightness level of the targeted or selected object by 8.
    UpMoves the targeted or selected texture up by 1 pixel.
    DownMoves the targeted or selected texture down by 1 pixel.
    LeftMoves the targeted or selected texture left by 1 pixel.
    RightMoves the targeted or selected texture right by 1 pixel.
    Shift+UpMoves the targeted or selected texture up by 8 pixels.
    Shift+DownMoves the targeted or selected texture down by 8 pixels.
    Shift+LeftMoves the targeted or selected texture left by 8 pixels.
    Shift+RightMoves the targeted or selected texture right by 8 pixels.
    Shift+RResets the texture offsets to 0, 0 on the targeted or selected object.
    +

    +
    + diff --git a/Help/editingmodes.html b/Help/editingmodes.html new file mode 100644 index 0000000..cbc3fd9 --- /dev/null +++ b/Help/editingmodes.html @@ -0,0 +1,24 @@ + + + + + About Editing Modes + + + + + + + + + + + +

    About Editing Modes

    + +
    +

    + With Doom Builder, you are always editing in a specific mode which depends on what you want to do and allows you to use the same mouse buttons and key controls for different purposes. You will find yourself switching editing modes all the time, so it is a good thing to remember which keys you have assigned to these modes. +

    +
    + diff --git a/Help/gameconfigs.html b/Help/gameconfigs.html new file mode 100644 index 0000000..8559e31 --- /dev/null +++ b/Help/gameconfigs.html @@ -0,0 +1,27 @@ + + + + + About Game Configurations + + + + + + + + + + + +

    About Game Configurations

    + +
    +

    + These configurations are the largest and most complex ones. A Game Configuration contains all the settings that determine Doom Builder's behavior when editing your map and optional features that are specific to certain map formats can be enabled or disabled here. A Game Configuration also keeps all information about hardcoded actions and things in the game, which Doom Builder cannot find in the WAD files. You can find the Game Configurations in "Configurations" directory, which is a subdirectory of your Doom Builder's program directory. Subdirectories are not searched by Doom Builder for configurations, but you can use them for include files. +

    +

    + Many settings in Doom Builder's user interface as bound to the available Game Configurations. This is because the user often wants to use different resources and compilers for a different project. When Doom Builder starts up and detects a new Game Configuration file, it will automatically create default settings for that configuration. The following default settings are read from the Game Configuration (only when first detected by Doom Builder): testparameters, testshortpaths, defaultsavecompiler, defaulttestcompiler, thingsfilters and texturesets. +

    +
    + diff --git a/Help/gc_basicsettings.html b/Help/gc_basicsettings.html new file mode 100644 index 0000000..502a341 --- /dev/null +++ b/Help/gc_basicsettings.html @@ -0,0 +1,132 @@ + + + + + Game Configuration - Basic Settings + + + + + + + + + + + +

    Game Configuration - Basic Settings

    + +
    +

    + type (string)
    + This indicates the type of configuration to prevent accedential use of a different configuration. Must always be the string "Doom Builder 2 Game Configuration".
    +
    + game (string)
    + The name that is displayed in Doom Buider for your Game Configuration.
    +
    + engine (string)
    + Game engine/sourceport name. This is used as the UDMF namespace for UDMF map format interface. It currently has no other function.
    +
    + defaultlumpname (string)
    + Default lump name suggested when creating a new map and selecting this configuration.
    +
    + testparameters (string)
    + Default parameters used to launch the test game engine. See Game Configurations window for the available parameter placeholders.
    +
    + testshortpaths (boolean)
    + Set to true to use MSDOS 8.3 format paths and filenames by default. Default is false. The user can still change this in the Game Configurations window.
    +
    + defaultsavecompiler (string)
    + Name of the Nodebuilder Compiler Configuration structure to use as the default settings for the compiler that is used when saving the map. The user can still change this in the Game Configurations window.
    +
    + defaulttestcompiler (string)
    + Name of the Nodebuilder Compiler Configuration structure to use as the default settings for the compiler that is used when testing the map. The user can still change this in the Game Configurations window.
    +
    + skills (structure)
    + This defines the skill options the user has available with this game engine/project. The settings in this structure are expected to be numbers with string values (the descriptive name for the skill level).
    +
    + Example: +

    +skills
    +{
    +	1 = "I'm too young to die";
    +	2 = "Hey, not too rough";
    +	3 = "Hurt me plenty";
    +	4 = "Ultra-Violence";
    +	5 = "Nightmare!";
    +}
    +
    +
    + linetagindicatesectors (boolean)
    + When true, Doom Builder will highlight sectors associated with the same tag number when a line is highlighted. This is only really useful for Doom format maps, because Hexen format and UDMF format has no single tag on linedefs (in those formats, the arguments of the linedef's action can be tags, which also works to highlight sectors). The default is false.
    +
    + soundlinedefflag (integer or string)
    + This lets Doom Builder know the linedef flag that indicates where sound should be blocked. Doom Builder uses this to give the line a special color and plugins can use this information to perform operations related to blocking sound lines. For map formats that use numeric flags (Doom and Hexen) this must be an integer specifying the flag value of the Block Sound flag. For map formats that use named flags (UDMF), this must be a string indicating the name of the Block Sound flag.
    +
    + singlesidedflag (integer or string)
    + This lets Doom Builder know the linedef flag that indicates a line with only one side. Doom Builder will set this flag value on a linedef when it changes a line to become single sided and removes the flag from a linedef when it becomes double sided. Plugins can also use this information to perform operations on linedefs. For map formats that use numeric flags (Doom and Hexen) this must be an integer flag value. For map formats that use named flags (UDMF), this must be a string indicating the name of the flag.
    +
    + doublesidedflag (integer or string)
    + This lets Doom Builder know the linedef flag that indicates a line with two sides. Doom Builder will set this flag value on a linedef when it changes a line to become double sided and removes the flag from a linedef when it becomes single sided. Plugins can also use this information to perform operations on linedefs. For map formats that use numeric flags (Doom and Hexen) this must be an integer flag value. For map formats that use named flags (UDMF), this must be a string indicating the name of the flag.
    +
    + impassableflag (integer or string)
    + This lets Doom Builder know the linedef flag that indicates a line which blocks players and monsters. Doom Builder uses this to give the line a special color and plugins can use this information to perform operations related to blocking sound lines. For map formats that use numeric flags (Doom and Hexen) this must be an integer specifying the flag value of the Impassable flag. For map formats that use named flags (UDMF), this must be a string indicating the name of the Impassable flag.
    +
    + makedoortrack (string)
    + Name of a texture to use on the walls when making a door.
    +
    + makedooraction (integer)
    + Linedef action number to put on the lines when making a door.
    +
    + makedoorarg# (0 .. 4) (integer)
    + Arguments for the linedef action number to put on the lines when making a door.
    +
    + generalizedlinedefs(boolean)
    + Set to true to support generalized linedef actions. This makes the gen_linedeftypes structure mandatory. Default value is false.
    +
    + generalizedsectors (boolean)
    + Set to true to support generalized sector effects. This makes the gen_sectortypes structure mandatory. Default value is false.
    +
    + doomlightlevels (boolean)
    + Set this to false to use linear lighting in Doom Builder. Normally Doom Builder uses a simulation of Doom's light levels. Default value is true.
    +
    + start3dmode (integer)
    + Thing type number that Doom Builder will use to keep your Visual Mode camera position stored in the map. Doom Builder will place a single thing of this type in your map and move it along as you move in Visual Mode.
    +
    + skyflatname (string)
    + Name of the flat that is interpreted as sky (meaning there is no ceiling). Doom Builder and plugins can use this information for various purposes.
    +
    + maxtexturenamelength (integer)
    + Maximum length of texture names in characters. This is used by Doom Builder to limit the input fields in the user interface and to check the validity of texture names in resources. This does NOT determine the actual limitation on the texture names in the map file format. Default value is 8.
    +
    + defaultthingflags (structure)
    + This defines what the default flags should be first the first new thing when inserted. In map formats that use numeric thing flags, the settings in this structure should be the numeric flags to set. In map formats that use named flags, the settings must be the names of the flags to set. The value of the settings is optional and is ignored by Doom Builder.
    +
    + Example for numeric flags: +
    +defaultthingflags
    +{
    +	1;
    +	2;
    +	4;
    +	32;
    +}
    +
    +
    + Example for named flags: +
    +defaultthingflags
    +{
    +	skill1;
    +	skill2;
    +	skill3;
    +	single;
    +	coop;
    +}
    +
    +
    + + +

    +
    + diff --git a/Help/gc_formatsettings.html b/Help/gc_formatsettings.html new file mode 100644 index 0000000..77d2145 --- /dev/null +++ b/Help/gc_formatsettings.html @@ -0,0 +1,163 @@ + + + + + Game Configuration - Map Format Settings + + + + + + + + + + + +

    Game Configuration - Map Format Settings

    + +
    +

    + formatinterface (string)
    + Interface class name in Doom Builder that is used to read and write the map data. +

      +
    • For Doom map format, use "DoomMapSetIO". This is a map format that uses numeric flags. +
    • For Hexen map format, use "HexenMapSetIO". This is a map format that uses numeric flags. +
    • For UDMF map format, use "UniversalMapSetIO". This is a map format that uses named flags. +
    +
    + gamedetect (structure)
    + This is used to determine if the Game Configuration is suitable for the opened WAD file. None of the settings in this structure have any impact on the actual editing, limitations or storage of the map, but only assist the user when opening WAD files that have not been opened with Doom Builder before (when no .dbs file is available). The setting names in this structure are the lump names that must be checked for. The value of these settings (integers) indicates which rule applies to the lump name for a positive check. Valid values are: +
      +
    • 1 - At least one of these lumps must exist. +
    • 2 - None of these lumps must exist. +
    • 3 - All of these lumps must exist. +
    + Example:
    +
    +gamedetect
    +{
    +	TEXTMAP = 2;
    +	ENDMAP = 2;
    +	EXTENDED = 2;
    +	BEHAVIOR = 3;
    +	E1M1 = 2;
    +	E1M2 = 2;
    +	E1M3 = 2;
    +	MAP01 = 1;
    +	MAP02 = 1;
    +	MAP03 = 1;
    +}
    +
    +
    + maplumpnames (structure)
    + This structure describes the lumps that make up the complete map, including scripts and nodes. It must also indicate what Doom Builder is supposed to do with these lumps and/or where the lumps come from. These lumps are normally written by the map format interface class, but some could be generated by the nodebuilder compiler or stored by Doom Builder directly. In this structure, there should be a structure for every map lump. The name of the structure should be the lump name, in uppercase. For the map header (which name differs) you can use the name ~MAP. The following settings should be in the lump structure (unless the default value applies):
    +
      +
    • required (boolean).
      + True to indicate that this lump is a required lump for the map. Default is false.
      +
      +
    • blindcopy (boolean).
      + Set this to true when Doom Builder should copy this lump along with the map without using it. Default is false.
      +
      +
    • nodebuild (boolean).
      + When set to true, this indicates that the nodebuilder compiler generates or modifies this lump. Default is false.
      +
      +
    • allowempty (boolean).
      + Set to true to allow the nodebuilder to leave this lump empty. Default is false.
      +
      +
    • script (string).
      + When this is set, it indicates that this lump can be edited with the script editor. The contents of this lump will be loaded in the script editor automatically. Set the value to the .cfg filename (without path) of a Script Configuration to use.
      +
    + Example:
    +
    +maplumpnames
    +{
    +	~MAP
    +	{
    +		required = true;
    +		blindcopy = true;
    +		nodebuild = false;
    +	}
    +	
    +	THINGS
    +	{
    +		required = true;
    +		nodebuild = true;
    +		allowempty = true;
    +	}
    +	
    +	LINEDEFS
    +	{
    +		required = true;
    +		nodebuild = true;
    +		allowempty = false;
    +	}
    +	
    +	SIDEDEFS
    +	{
    +		required = true;
    +		nodebuild = true;
    +		allowempty = false;
    +	}
    +	
    +	VERTEXES
    +	{
    +		required = true;
    +		nodebuild = true;
    +		allowempty = false;
    +	}
    +	
    +	SEGS
    +	{
    +		required = false;
    +		nodebuild = true;
    +		allowempty = false;
    +	}
    +	
    +	SSECTORS
    +	{
    +		required = false;
    +		nodebuild = true;
    +		allowempty = false;
    +	}
    +	
    +	NODES
    +	{
    +		required = false;
    +		nodebuild = true;
    +		allowempty = false;
    +	}
    +	
    +	SECTORS
    +	{
    +		required = true;
    +		nodebuild = true;
    +		allowempty = false;
    +	}
    +	
    +	REJECT
    +	{
    +		required = false;
    +		nodebuild = true;
    +		allowempty = false;
    +	}
    +	
    +	BLOCKMAP
    +	{
    +		required = false;
    +		nodebuild = true;
    +		allowempty = false;
    +	}
    +	
    +	SCRIPTS
    +	{
    +		required = false;
    +		nodebuild = false;
    +		script = "ZDoom_ACS.cfg";
    +	}
    +}
    +
    +
    +

    +
    + diff --git a/Help/gc_resourcesettings.html b/Help/gc_resourcesettings.html new file mode 100644 index 0000000..9852425 --- /dev/null +++ b/Help/gc_resourcesettings.html @@ -0,0 +1,126 @@ + + + + + Game Configuration - Resource Settings + + + + + + + + + + + +

    Game Configuration - Resource Settings

    + +
    +

    + decorategames (string)
    + Fill this to the game names to support DECORATE actors from. Only the DECORATE actors who's game name is in this string will be loaded. If this setting is not set, DECORATE lumps are not loaded.
    +
    + Example: +

    +decorategames = "heretic raven";
    +
    +
    + mixtexturesflats (boolean)
    + Doom Builder will allow the use of flats on walls and textures on floors when this is set to true. Textures and flats will be mixed with priority to the original purpose. For textures this means that textures override flats with the same name, for flats this means that flats override textures with the same name. Default is false.
    +
    + defaulttexturescale (decimal)
    + The scale of textures when no scale is known. This is a scalar value: 0.5 is half the original size and double the resolution. Default is 1.0.
    +
    + defaultflatscale (decimal)
    + The scale of flats when no scale is known. This is a scalar value: 0.5 is half the original size and double the resolution. Default is 1.0.
    +
    + scaledtextureoffsets (boolean)
    + Determines if texture offsets are in world coordinates (unscaled by texture scale) or texture coordinates. Set to true to use texture coordinates. Default is false.
    +
    + textures (structure)
    + This lists the marker lump names that indicate the begin and end of a list of textures that Doom Builder should load. There must be a separate structure for each range, for which the structure name doesn't matter. The range must have a 'start' setting and an 'end' setting of which the values must be the names of the start and end lumps (strings). Please note that PNAMES, TEXTURE1 and TEXTURE2 lumps do not need to be in the game configuration, they are always loaded when available.
    +
    + Example: +
    +textures
    +{
    +	zdoom1
    +	{
    +		start = "TX_START";
    +		end = "TX_END";
    +	}
    +}
    +
    +
    + patches (structure)
    + This lists the marker lump names that indicate the begin and end of a list of patches that Doom Builder should load. There must be a separate structure for each range, for which the structure name doesn't matter. The range must have a 'start' setting and an 'end' setting of which the values must be the names of the start and end lumps (strings). Note that Doom Builder does not load all patches, only those that are used by the textures.
    +
    + Example: +
    +patches
    +{
    +	standard1
    +	{
    +		start = "P_START";
    +		end = "P_END";
    +	}
    +	
    +	standard2
    +	{
    +		start = "PP_START";
    +		end = "PP_END";
    +	}
    +}
    +
    +
    + sprites (structure)
    + This lists the marker lump names that indicate the begin and end of a list of sprites that Doom Builder should load. There must be a separate structure for each range, for which the structure name doesn't matter. The range must have a 'start' setting and an 'end' setting of which the values must be the names of the start and end lumps (strings). Note that Doom Builder does not load all sprites, only those that are used by the things.
    +
    + Example: +
    +sprites
    +{
    +	standard1
    +	{
    +		start = "S_START";
    +		end = "S_END";
    +	}
    +}
    +
    +
    + flats (structure)
    + This lists the marker lump names that indicate the begin and end of a list of flats that Doom Builder should load. There must be a separate structure for each range, for which the structure name doesn't matter. The range must have a 'start' setting and an 'end' setting of which the values must be the names of the start and end lumps (strings).
    +
    + Example: +
    +flats
    +{
    +	standard1
    +	{
    +		start = "F_START";
    +		end = "F_END";
    +	}
    +}
    +
    +
    + colormaps (structure)
    + This lists the marker lump names that indicate the begin and end of a list of colormaps that Doom Builder should load. There must be a separate structure for each range, for which the structure name doesn't matter. The range must have a 'start' setting and an 'end' setting of which the values must be the names of the start and end lumps (strings).
    +
    + Example: +
    +colormaps
    +{
    +	standard1
    +	{
    +		start = "C_START";
    +		end = "C_END";
    +	}
    +}
    +
    +
    + + +

    +
    + diff --git a/Help/header.gif b/Help/header.gif new file mode 100644 index 0000000000000000000000000000000000000000..e180c74dbb98b4f631f746554a6466959fffb3c0 GIT binary patch literal 2134 zcmWmDdsLJ69mnwm5)$rrL;<;Jp^6wntwgLQKoC(8(4At33c{+TZb!te)#kWxXUgOv zw6yh93#|ubI}6jT9*>&@a!;N-d6GPN@+2Ym+mj20)=r%a_V7Ee^FE*R{r`99aAgUj zd6KY;@RC5_KYu97MLXlJ`mWo7DkywTki-C$@g|JAh6N=0tAtaV+#1cp<*GXk6 zIiQsTDnJP-K?npPK#5A_2C2*(L+{mDZ+1d@79>f_)H-sdavw2Jx$rfCdTR z7Z~wr5w8}uYV{tS!2=uo8ht>c$6&o*i~3;Hj~IPALqLlLU^ECDf;z*J&KN*U)JV7F}Wn{9zbYrtZ6SZxlg z-C=V$Z8oRP?zA~vHoMD?b2#k|x5MGKJ6v|B%i(lyaCw|gkJIIL;=>)VI)Zjb&}R2L zoI$HIXma13Za3!kU>=VT=k^4h?j@HS^LTw8uit|O-QFb+=J$I2Ud-pk z0vHz9s4wWl`Ir3uB@Fv95Lm&mI6N6y8iNZZk<{r^G{9;l5nTUs1;O>=O?gU#q?uClLvf zF-8-W@p(zd!z;N>_mAJrSjN(;U)aa*R-I4bz5n>Zi9YFQHJNLB0MCSOJ~|!|J%dc@ zMiagi?>+_It@-yu;OdJ!N>|PB(}pAOf1eyubY7X`4f$x~DYyKmZbShj-w#=I{&H$3 zujTXKr0itBe)5wz`!M;=BgNI+=P`%F8~jJNOx^k}$~}JO*z75F%zqo?x2i}V^`lQR zFTdJMXkW5~C$1I{o|VjZI7jpP&p%_ZII5}0-Sfx0eGqw{w)GF)x{l z))bsu{b3~Kxg_ChR%-Ra=`JLrXQ=4n$#&JAm1lL|_HM3g?kh&pPYUUQ)ts0Cdq%~P zJr^H0)iRVOP46EI&S^bx;K|n>dJ!HBdNa{=_x@> z4YhI1szk6Tv*w+fbo)ha=V$BriHiL^4s+W?@}Bl`+Kag|&%raCtV)KK35{3$=gMr% zmPj76mT|eUJUThCK$gdDzQ7}w%AeH@)04CKB6=IC=!1&ik){-s^|;0&)&AMl%Rm~F z$93J0CwFwl%f}LL7K6<^R?2B!d_|;!AvnJ4oM5y&B+~RE_P}gwIZ$-=qLuu&WT1mj z`E`27(M>CHV|}3xJFhb&WBB&*y~axmz`NM=l(IB!UL&9|I9Yt+KB@qG5NP`ONL+Fh zwUB+Jfft(koc74`kIaHBA~|M)u(0)$fK8lv{S}eA^_o6jl6!la))^YJZFr6dY)d)9 zP)ndNasllXt=v|-%t|<0Q5?C8#8}`*>xW7jY5WRu*kkL@p1kdkdqPT0%jLW5Jbq~A_=T?O zLId~FkEKhT@~9nO4Nl5-1b&!bd%MTkd?>a_Kvx#Gq{oxOD5IRLH1BfYjeasDtr49k9z!0PU1uf zdB+)6#7uiv!uK>%^tDTU4V>o4atch^X7Y%P-*Hq8XW>SNDKnq%4~m@NnThtfHn880Wo zlG@_G=3JSN9bmGE9BM_;ntfC2#NLpoYHojZPuZq{31WEDrSM99Cn1PS5J( ewh&mJC>ocj3S_>_SJ6fH!qPsq9wZV8rT+(KK*(GG literal 0 HcmV?d00001 diff --git a/Help/introduction.html b/Help/introduction.html new file mode 100644 index 0000000..0e6b4a6 --- /dev/null +++ b/Help/introduction.html @@ -0,0 +1,26 @@ + + + + + Introduction + + + + + + + + + + +

    Introduction

    + +
    +

    + Doom Builder is an advanced, revolutionary map editor for Doom and games based on the Doom engine, such as Heretic, Hexen and Strife. This editor is highly extendible for the different game engines of the Doom community. Doom Builder introduced the 3D editing mode in the Doom community and is still the leading editor for Doom levels today. + +

    Reference Manual

    + This is the Reference Manual for Doom Builder 2. You can use this manual to look up how the editor works and what the specific modes and actions do. This manual is not a beginners tutorial that teaches you how to make maps. +

    +
    + diff --git a/Help/scriptingconfigs.html b/Help/scriptingconfigs.html new file mode 100644 index 0000000..9739b98 --- /dev/null +++ b/Help/scriptingconfigs.html @@ -0,0 +1,24 @@ + + + + + About Scripting Configurations + + + + + + + + + + + +

    About Scripting Configurations

    + +
    +

    + B +

    +
    + diff --git a/Help/scripts.js b/Help/scripts.js new file mode 100644 index 0000000..8abb6b7 --- /dev/null +++ b/Help/scripts.js @@ -0,0 +1,10 @@ + +function autoadjustheight(iframeid) +{ + // Find the height of the internal page + var doc_height = document.getElementById(iframeid).contentWindow.document.body.scrollHeight; + + // Change the height of the iframe + document.getElementById(iframeid).height = doc_height; +} + diff --git a/Help/template.html b/Help/template.html new file mode 100644 index 0000000..47dfe71 --- /dev/null +++ b/Help/template.html @@ -0,0 +1,23 @@ + + + + + Template + + + + + + + + + + +

    Template

    + +
    +

    + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque dignissim porttitor nisl. Sed bibendum magna nec quam. Phasellus purus. Nunc rutrum libero nec libero. Fusce iaculis elit sed nibh. Curabitur posuere magna at massa suscipit consequat. Duis ac quam. Ut ante nulla, tincidunt et, tincidunt at, convallis eget, diam. Duis et ligula. Mauris accumsan massa id nunc. Donec sit amet ligula eget odio mattis dictum. Donec mauris nunc, vulputate nec, cursus ac, ullamcorper ornare, neque. +

    +
    + diff --git a/Help/terminology.html b/Help/terminology.html new file mode 100644 index 0000000..c3f91b8 --- /dev/null +++ b/Help/terminology.html @@ -0,0 +1,59 @@ + + + + + Terminology + + + + + + + + + + + + + + + + + + + + + +

    Terminology

    + +
    +

    +

    Resource

    + A file or directory from which textures, flats, sprites and other information is read that is used during map editing. Doom Builder supports WAD files, PK3 files and directories as resources. + +

    WAD

    + A WAD file is a collection of data, which can include textures, sprites, sounds but also maps. This is the most common resource type used by Doom. A WAD file can be an IWAD (Internal WAD) which contains all data needed to run a game without any additional WAD files, or a PWAD (Patch WAD) which only contains the data you wish to change (for example; a new map and some textures only, but no change in sounds or sprites). Doom always requires a single IWAD and optionally one or more PWADs. + +

    PK3

    + The PK3 file is actually a ZIP file and contains a directory with a specific structure. It can be used as a replacement for WAD files in some Doom sourceports and is smaller than the WAD file because it is compressed. Doom Builder can read from PK3 files as resources, but for better performance it is recommended that you unzip your PK3 file (keeping the directory structure) and add the directory as resource. For more information, see Using ZIPs as WAD replacement for more information. + +

    Element

    + A map consists of vertices, linedefs, sidedefs, sectors and things. These are all elements in a map and each has their own set of properties. Some elements are connected to other elements: A linedef is always connected to two vertices and has one or two sidedefs. A sidedef is always connected to one sector and a sector has one or more sidedefs. + +

    Vertex

    + This is the most simple element in a map. A vertex is a point on the map which has X and Y coordinates. + +

    Linedef

    + This is a line in your map geometry which connects two vertices. Every wall and sector border must have a linedef. Linedefs can have an action that is triggered when player pushes the wall or walks over the line. The linedef has a front (right) and a back (left) side where a sidedef can be attached. The front side should always have a sidedef attached, but when the linedef is a wall with nothing behind it (void), it should not have a back side. + +

    Sidedef

    + A sidedef defines how one side of a linedef looks like and which sector it forms. A sidedefs has a upper, middle and lower texture which are sometimes required depending on the heights of the sectors and all share the same texture offsets. + +

    Sector

    + The sector defines an area on the map. It has properties for the floor and ceiling and can have special effects for the environment. The shape of the sector is defined by the sidedefs and the linedefs they are on and should always be a closed area or multiple closed areas. There are exceptional cases, however, where authors use a non-closed sector to create a special effect by exploiting the Doom engine. + +

    Thing

    + Things are basically any object in the map that is not part of its geometry. They can be decorations, items, monsters, player starts or even indicators for the sourceport to do something in that location. They have X and Y coordinates and in Hexen format they also have a Z coordinate (which often is relative to the sector floor). +

    +
    + diff --git a/Help/userinterface.html b/Help/userinterface.html new file mode 100644 index 0000000..ff99afb --- /dev/null +++ b/Help/userinterface.html @@ -0,0 +1,24 @@ + + + + + About the User Interface + + + + + + + + + + + +

    About the User Interface

    + +
    +

    + Doom Builder's user interface exists of one main editing window in which most of the map editing will be done. Doom Builder also has a window for scripting and several built-in dialogs for editing and setting up your preferences and much more. This part of the Reference Manual will help you explain all the buttons and controls on every window. +

    +
    + diff --git a/Help/w_customfields.html b/Help/w_customfields.html new file mode 100644 index 0000000..17f267f --- /dev/null +++ b/Help/w_customfields.html @@ -0,0 +1,24 @@ + + + + + Custom Fields Editor + + + + + + + + + + + +

    Custom Fields Editor

    + +
    +

    + The UDMF format allows any number of custom properties on every element in the map. With the custom fields editor, you can edit the known properties and add custom ones yourself. The game configuration has known properties that are supported by the sourceport and are automatically shown in the list. Grayed items are known, but have not been set (and thus have their default value). To add your own custom properties, you can click on the text Click to add custom field and type the name for the new property. Then you can change the type and set its value. To remove a custom property, just select it and press Delete. Known properties cannot be deleted, pressing Delete with a known property selected will just reset it back to its default value. +

    +
    + diff --git a/Help/w_errorsandwarnings.html b/Help/w_errorsandwarnings.html new file mode 100644 index 0000000..0237c47 --- /dev/null +++ b/Help/w_errorsandwarnings.html @@ -0,0 +1,27 @@ + + + + + Errors and Warnings Window + + + + + + + + + + + + +

    Errors and Warnings Window

    + +
    +

    + When errors or warnings occur during certain operations these will be reported in this window. If the window does not automatically show, you can find it in the Tools menu or by pressing the default key F11. Click the Copy Selection button to copy the selected errors to the clipboard. Click the Clear button to remove all the errors from the window.
    +
    + You can choose to open this window automatically when errors occur after operations such as loading a map by checking the option Show this window when errors occur. +

    +
    + diff --git a/Help/w_gameconfigurations.html b/Help/w_gameconfigurations.html new file mode 100644 index 0000000..48ec579 --- /dev/null +++ b/Help/w_gameconfigurations.html @@ -0,0 +1,86 @@ + + + + + Game Configurations Window + + + + + + + + + + + + + + + + + +

    Game Configurations Window

    + +
    +

    + The Game Configurations window is accessible from the Tools menu, or with the default key F6. On the left of this window you can select the game configuration for which you want to change settings. Choose from the tabs on the right what you want to change. + +

    Resources

    + This is a list of resources that will be loaded before any other resources. You should add the IWAD and other project-related resources here so that they are automatically loaded with every map you make with this game configuration. These resources, from top to bottom, will be loaded first before any other resources. You can drag the resource items to change their order. See also the Resource Options Window. + +

    Nodebuilder

    + Building the BSP tree and other additional information such as BLOCKMAP and REJECT is useful to optimize the in-game performance and for most older sourceports and vanilla Doom it is even required. This is where you select the nodebuilder you want to run in certain cases and which settings to use. You can choose a nodebuilder to run when saving the map and when testing the map. Consult the documentation of the sourceport you are using to find out which nodebuilder and which settings you need. Advanced users may want to add more specific settings by writing a nodebuilder configuration. + +

    Testing

    + No matter how well an editor works, testing to see how your map looks and performs in the sourceport is essential. Here you can choose the sourceport to test with and the parameters to use. You can also set the default skill level, but this can also be easily changed from the test dropdown menu in the toolbar of the main window. You generally do not need to specify custom parameters (the default ones should work fine) but advanced users may wish to change these parameters.
    +
    + The following special placeholders can be used in the parameters; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    %FWAD file with the map that is to be tested. NOTE: this is a temporary file and not the file you opened or saved.
    %WPIWAD resource file with full path included. This is the first (highest) IWAD file that is found in the resources list.
    %WFIWAD resource filename only, without path. This is the first (highest) IWAD file that is found in the resources list.
    %LMap lump name as is set in the map options window.
    %L1The first number found in the map lump name (as is set in the map options window). This is for use with the -warp parameter.
    %L2The second number found in the map lump name (as is set in the map options window). This is for use with the -warp parameter.
    %APAll resource files, except the first IWAD, with full paths included. The resources are separated by spaces and when this placeholder is enclosed within quotes ("%AP") then the quotes are repeated for every resource.
    %SSkill number at which to test.
    %NMThis is either -nomonsters when you choose to test without monsters, or nothing at all.
    + When the checkbox Use short path and file names (MSDOS 8.3 format) is checked, all the above placeholders that output filenames and/or paths will use the short version that is compatible with MSDOS. + +

    Textures

    + You can group your textures into categories called Texture Sets. There are some fixed Texture Sets that appear in the browsers automatically (such as the "All" set and the sets for each resource) and you can create custom Texture Sets here. You can also copy Texture Sets from one game configuration to another with the Copy and Paste buttons. With the Add Default Sets button you can add the default Texture Sets that are provided with the game configuration (if any). For more information about creating your own Texture Sets, see the Texture Set window. + +

    Modes

    + Here you can choose what editing modes you can use when editing with the selected game configuration. This is useful when plugins are installed that replace certain editing modes or plugins that add editing modes for a specific sourceport only. Note that some editing modes may require other editing modes from the same plugin to work together. When plugins are installed, you should consult the provided documentation to see what changes should be made here. Only recommended for advanced users. +

    +
    + diff --git a/Help/w_gridsetup.html b/Help/w_gridsetup.html new file mode 100644 index 0000000..44bc577 --- /dev/null +++ b/Help/w_gridsetup.html @@ -0,0 +1,27 @@ + + + + + Grid Setup Window + + + + + + + + + + + + +

    Grid Setup Window

    + +
    +

    + On this window you can configure the grid and background settings. Click the grid icon in the statusbar or select Grid Setup from the Edit menu to access this window. The grid helps aligning your map elements and the 64 mappixels grid is important to Doom as it indicates how flats are aligned. You can change the size of the grid you want to work with at the top of this window. Note that the grid can also easily be changed with the actions Grid Increase and Grid Decrease.
    +
    + The background is useful when drawing over an image to copy its shape. Check the Show background image checkbox and select a texture, flat or file to be used as the background image. You can also change the offset and the scale. +

    +
    + diff --git a/Help/w_imagesbrowser.html b/Help/w_imagesbrowser.html new file mode 100644 index 0000000..3332b06 --- /dev/null +++ b/Help/w_imagesbrowser.html @@ -0,0 +1,27 @@ + + + + + Image Browser Window + + + + + + + + + + + + +

    Image Browser Window

    + +
    +

    + Use this browser to look for textures or flats. On the left you have the list with your Texture Sets. The list also contains a set named "All" which, obviously, contains all textures or flats. A Texture Set for each loaded resource has also been added. This is useful if you know in which resource the image resides. Once you have chosen your Texture Set, you can use the text field at the bottom to enter the texture name. You only have to enter the name partly and the list of images will adjust to show only the images that match with the entered name. Select an image to view its dimensions at the bottom. When the focus is on the images list, you can press the Tab key to jump between the same image in the "Used textures" and "Available textures" areas. Doubleclick the image or click OK to make your selection or Cancel to close the list.
    +
    + To make your own Texture Sets, see the Textures tab on the Game Configurations Window. +

    +
    + diff --git a/Help/w_linedefedit.html b/Help/w_linedefedit.html new file mode 100644 index 0000000..18ce929 --- /dev/null +++ b/Help/w_linedefedit.html @@ -0,0 +1,48 @@ + + + + + Linedef Properties Window + + + + + + + + + + + + + + + + + + +

    Linedef Properties Window

    + +
    +

    + With this dialog window you can edit all linedef properties. When a selection of multiple linedefs is made, some fields may appear grayed or empty in case they are different for some of the selected elements. Setting a value in grayed or empty field will apply this to the entire selection. + +

    Properties

    + On this tab you will find all the general settings for the linedef. These setting apply to both sides of the linedef, unless specified otherwise.
    +
    + The Settings area provides options for the line such as Impassable which blocks players and monsters from crossing the line, and Doublesided which is automatically set or unset by Doom Builder to indicate if the line has one or two sidedefs. Depending on the game configuration, this area can feature many more options for the behaviour of the linedef.
    +
    + Below that is the Action area, which allows you to set a special action that is executed on a specific trigger method such as a switch or proximity trigger. In Hexen format, the action can take some arguments and the trigger method can be specified independently from the action. In UDMF format, the trigger method can be any combination of methods.
    +
    + In Doom and UDMF formats, the line can also be tagged with a number that identifies the linedef. This number can then be used by Thing actions or scripts to perform actions on the linedef. + +

    Sidedefs

    + Here you can change the properties for each side of the line. If you wish to add or remove a sidedef on the linedef, just check or uncheck the Front Side or Back Side box. In the Sector Index field you can type the sector index that you which this sidedef to be bound to. Note that you generally do not have to change this as Doom Builder will make valid sectors for you. Changing the Sector Index is only recommended for advanced users. On the right are the textures that are on the sidedef. Left-click on the box to browse for a texture using the Image Browser, right-click on the box to clear the texture (making it a single dash). If you know the texture name, you can also type the name directly into the text field below the texture box.
    +
    + In UDMF format, you can click the Custom Fields buttons to change the custom fields for the sidedefs. See Custom Fields Editor for more information. + +

    Custom

    + This tab allows you to edit the custom fields on the linedef. Only available in UDMF format. See Custom Fields Editor for more information. +

    +
    + diff --git a/Help/w_mainwindow.html b/Help/w_mainwindow.html new file mode 100644 index 0000000..a5bee54 --- /dev/null +++ b/Help/w_mainwindow.html @@ -0,0 +1,37 @@ + + + + + Main Window + + + + + + + + + + +

    Main Window

    + +
    +

    The main interface window consists of the following parts;

    + +

    +

    Menus

    + The menus are very straight-forward and you'll find most common features in the usual places. File related actions in the File menu, view related actions in the View menu and editing related actions in the Edit menu. + +

    Toolbar

    + Your most needed actions and options are on the toolbar, right below the menu. + +

    Information panel

    + When highlighting or targeting a specific element in the working area, a small summary about that element is displayed in this panel. You can minimize the information panel by clicking on the little arrow button on the right to maximize your working area. + +

    Statusbar

    + Doom Builder shows you the current status in the statusbar. On the left is a small LED that turns green when Doom Builder is idle. When the LED turns yellow, it means Doom Builder is doing some background work. When the LED is red, then Doom Builder is busy performing an action. Right next to the status LED is a description of the current status. It also shows a result description and flashes the LED when performing and action.
    +
    + On the right of the statusbar are grid size (in mappixels), the zoom percentage and the current mouse coordinates. You can click the buttons next to the grid size and the zoom percentage to bring up a related menu that allows you to change any of these settings. +

    +
    + diff --git a/Help/w_mainwindow.png b/Help/w_mainwindow.png new file mode 100644 index 0000000000000000000000000000000000000000..cecbf9d4b104cbd4c185e68a310a663fc8de219c GIT binary patch literal 64593 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 Yp*cfv+JJOf3eeAz6qOUH6EX<;58GIm2mk;8 literal 0 HcmV?d00001 diff --git a/Help/w_mapoptions.html b/Help/w_mapoptions.html new file mode 100644 index 0000000..6f7a13d --- /dev/null +++ b/Help/w_mapoptions.html @@ -0,0 +1,30 @@ + + + + + Map Options Window + + + + + + + + + + + + +

    Map Options Window

    + +
    +

    + This dialog window allows you to change some options that apply to the entire map and how it is loaded. You can access it from the Edit menu, or with the default key F2. At the top you can choose the Game Configuration that you wish to use when editing this map. Please note that changing to a configuration of a different file format may cause a loss of information in your map, possibly breaking it. Below the Game Configuration field is the Level Name field. Here you can enter the lump name that is used to store your map in the WAD file. An example level name for a Doom 1 map could be E2M3 which indicates the map is or episode 2, level 3. An example for a Doom 2 map is MAP03 which is level 3. + +

    Resources

    + The Resources area allows you to specify which resources are loaded when editing this map. The Strictly load patches between P_START and P_END only for this file checkbox applies to the wad file you are editing (where your map is in). When this is checked, Doom Builder will resolve texture patch conflicts by strictly loading patch lumps only from between the P_START and P_END lumps as it was intended by id Software, but most sourceports do not require this (as result, older maps have texture patch lumps that are not in between P_START and P_END).
    +
    + In the list of resources you can see all the resources that will be loaded. Doom Builder shows the items from the game configuration in gray at the top of the list, because those are always loaded first. You can change the resources loaded according to the game configuration in the Game Configurations Window. You can drag the resource items (except the ones from the game configuration) to change their order. See also the Resource Options Window. +

    +
    + diff --git a/Help/w_openmapoptions.html b/Help/w_openmapoptions.html new file mode 100644 index 0000000..4c93e67 --- /dev/null +++ b/Help/w_openmapoptions.html @@ -0,0 +1,28 @@ + + + + + Open Map Window + + + + + + + + + + +

    Open Map Window

    + +
    +

    + When opening a map from a WAD file, this is the dialog that lets you choose what level to open and what options to use. At the top you can choose the Game Configuration that you wish to use when editing this map. When you select the game configuration you need, Doom Builder will show you a list of maps from the WAD file that can be opened with the selected configuration. You must select a map from the list to open. + +

    Resources

    + The Resources area allows you to specify which resources are loaded when editing this map. The Strictly load patches between P_START and P_END only for this file checkbox applies to the wad file you are editing (where your map is in). When this is checked, Doom Builder will resolve texture patch conflicts by strictly loading patch lumps only from between the P_START and P_END lumps as it was intended by id Software, but most sourceports do not require this (as result, older maps have texture patch lumps that are not in between P_START and P_END).
    +
    + In the list of resources you can see all the resources that will be loaded. Doom Builder shows the items from the game configuration in gray at the top of the list, because those are always loaded first. You can change the resources loaded according to the game configuration in the Game Configuration Window. You can drag the resource items (except the ones from the game configuration) to change their order. See also the Resource Options Window. +

    +
    + diff --git a/Help/w_preferences.html b/Help/w_preferences.html new file mode 100644 index 0000000..15783e5 --- /dev/null +++ b/Help/w_preferences.html @@ -0,0 +1,56 @@ + + + + + Preferences Window + + + + + + + + + + +

    Preferences Window

    + +
    +

    + The Preferences window provides options that apply to Doom Builder, regardless of the game configuration or map you choose to edit. You can access this window from the Tools menu, or with the default key F5. The options are categorized in different tabs; + +

    Interface

    + This tab contains some interface related options. +

    Default view:

    + This sets the default view mode that you wish to use when opening or creating a new map. +

    Preview image size:

    + The size of all preview images in the editor and in the Image Browser. Smaller preview images consume less system memory. +

    Autoscroll speed:

    + During some actions, such as drawing and dragging, Doom Builder can automatically scroll the working area when you move your mouse towards the edges. Use this setting to set the scrolling speed to use. Drag the slider completely to the left to turn off automatic scrolling. +

    Script Editor:

    + Here you can set the font and other options regarding the Script Editor. Check the box Always on top of main window if you want the script editor to float above the Main Window even when the script editor does not have the focus. +

    Visual Modes:

    + This area contains several settings for the Visual Mode. To improve performance, you may want to decrease the View distance setting. + +

    Controls

    + This is where you configure your keyboard and mouse shortcut keys. On the left is a list of all Actions in Doom Builder with the current mouse or key combination shown next to them. Select an action to view a summary of this action on the right. Press any key combination to set to this action, or select a special mouse combination from the dropdown box. Below that, you can see which other actions share the same key combination so that you can verify any conflicts it may have. Note that actions used in different editing modes can safely share the same key combination. + +

    Appearance

    + On this tab you can change some settings related to visual aesthetics. On the top and the left of this tab you can change colors that are used in the Script Editor and Working Area. +

    Passable lines transparency:

    + This controls how much translucent the passable lines are. 0% is completely opaque, which is the same as impassable lines. Passable or impassable can be set with a linedef setting, but is automatically set by the editor for singlesided lines and removed for doublesided lines. +

    Textures and Flats brightness:

    + With this setting you can increase the brightness of the textures and flats. This is useful when viewing the texture against a white background, or if you are using a dark monitor. +

    Black background in image browser:

    + Turn this on to create a black background in the Image Browser. +

    Bilinear filtering in classic modes:

    + This enables bilinear texture filtering for all classic Editing Modes (2D). Bilinear texture filtering removes the large square blocks when zooming in, but can also make it look blurry. +

    Bilinear filtering in visual modes:

    + This enables bilinear texture filtering for all visual Editing Modes (3D). Bilinear texture filtering removes the large square blocks when zooming in, but can also make it look blurry. +

    High quality display rendering:

    + This improves the display with the use of Pixel Shader Model 2.0. Also adds anti-aliasing to the classic Editing Modes (2D). Performance may be improved by turning off this setting (this also gives a more oldschool Doom Builder 1.68 look to the working area). When Pixel Shader Model 2.0 support is not available, Doom Builder automatically turns off this setting and disables this option. +

    Square things:

    + Things in Doom use a square shape for collision detection and you may want to turn this option on to get a better view on the bounding boxes of things in the classic Editing Modes (2D). +

    +
    + diff --git a/Help/w_resourceoptions.html b/Help/w_resourceoptions.html new file mode 100644 index 0000000..233c407 --- /dev/null +++ b/Help/w_resourceoptions.html @@ -0,0 +1,36 @@ + + + + + Resource Options Window + + + + + + + + + + + + + +

    Resource Options Window

    + +
    +

    + Use this dialog window to browse for a resource and change any options on how to use the resource. Doom Builder supports 3 types of resources; + +

    WAD File

    + This is the most common resource known with Doom. You can find more information about the WAD format on the Doom Wiki. When the Strictly load patches between P_START and P_END only for this file checkbox is checked, Doom Builder will resolve texture patch conflicts by strictly loading patch lumps only from between the P_START and P_END lumps in this WAD file as it was intended by id Software, but most sourceports do not require this (as result, older maps have texture patch lumps that are not in between P_START and P_END).
    + +

    Directory

    + Doom Builder can load resources from a directory in different ways. The directory is expected to use the PK3 directory structure (for more information see the ZDoom Wiki) but you can use it to simply load images from the directory root as textures or flats by turning on the options Load images in directory root as textures/flats. + +

    PK3 File

    + The PK3 file is a zipped directory structure and can be used as WAD file replacement by several sourceports. For more information see the ZDoom Wiki. + +

    +
    + diff --git a/Help/w_scripteditor.html b/Help/w_scripteditor.html new file mode 100644 index 0000000..2183e4a --- /dev/null +++ b/Help/w_scripteditor.html @@ -0,0 +1,51 @@ + + + + + Script Editor Window + + + + + + + + + + +

    Script Editor Window

    + +
    +

    The script editor is a powerful text editor with syntax highlighting and autocomplete. It allows you to open and edit multiple documents at the same time, even if they do not reside in your map.

    + +

    + Each opened script has its own tab page. When there are text lumps in the map format (such as the SCRIPTS lump in Hexen formats), they will be opened automatically and cannot be closed. +

    Shortcut Keys

    + + + + + + + + + + + + + + + + + + + + + + + + + +

    + + diff --git a/Help/w_scripteditor.png b/Help/w_scripteditor.png new file mode 100644 index 0000000000000000000000000000000000000000..440d8d6d587b0689465f1a96c9151f0b8c1470b6 GIT binary patch literal 55223 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

    Q}H$?*}*>g#hIDKu79*;`xfyLWdj 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

    XyiGp9l|I}*xSa>y9
    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
    +
    +
    +	
    +	Sector Properties Window
    +	
    +	
    +	
    +
    +
    +	
    +	
    +	
    +	
    +	
    +	
    +	
    +	
    +	
    +	
    +	
    +	
    +	

    Sector Properties Window

    + +
    +

    + On this dialog you can change all the sector properties. When a selection of multiple sectors is made, some fields may appear grayed or empty in case they are different for some of the selected elements. Setting a value in grayed or empty field will apply this to the entire selection. + +

    Properties

    + You can manually set the Ceiling height and Floor height at the top of this window. On the right are the flats (textures) that are on the floor and ceiling. Left-click on the box to browse for a flat using the Image Browser. If you know the flat name, you can also type the name directly into the text field below the flat box.
    +
    + In the Effects area you can set a special effect for the sector and set the Brightness level to use.
    +
    + Below that you can enter a tag number to identify the sector. This number can then be used by Linedef actions or scripts to perform actions on the sector. + +

    Custom

    + This tab allows you to edit the custom fields on the sector. Only available in UDMF format. See Custom Fields Editor for more information. +

    +
    + diff --git a/Help/w_textureset.html b/Help/w_textureset.html new file mode 100644 index 0000000..48d4dea --- /dev/null +++ b/Help/w_textureset.html @@ -0,0 +1,29 @@ + + + + + Texture Set Window + + + + + + + + + + +

    Texture Set Window

    + +
    +

    + This dialog window lets you make your own Texture Set. On the top of this window you can enter the name of the Texture Set, this is how it will be displayed in the Game Configurations Window and the Image Browser Window. It is recommended that you make your Texture Sets while the map you are editing is open in Doom Builder, so that you can see the results immediately. + +

    Filters

    + In this area you set up what you want to include in this Texture Set. You can use wildcards to include more than one texture at once. Use the question mark ? to indicate exactly one character (no matter which character) and use the asterisk * to indicate zero or more characters (no matter which characters). Each Texture Set has its own filters, so it is possible for Texture Sets to overlap and include textures that are also in other Texture Sets. + +

    Results

    + If you have a map open in Doom Builder while you are setting up your Texture Sets, you can instantly see the result in the Results area. By clicking the buttons Show Matches and Show Not Matching you can see which textures are included and which are not. When viewing the texture that are not included, you can double-click the texture image to add it to the filters list and include it in the Texture Set. +

    +
    + diff --git a/Help/w_thingedit.html b/Help/w_thingedit.html new file mode 100644 index 0000000..0fac378 --- /dev/null +++ b/Help/w_thingedit.html @@ -0,0 +1,36 @@ + + + + + Thing Properties Window + + + + + + + + + + + + +

    Thing Properties Window

    + +
    +

    + This dialog allows you to edit all the thing properties. When a selection of multiple things is made, some fields may appear grayed or empty in case they are different for some of the selected elements. Setting a value in grayed or empty field will apply this to the entire selection. + +

    Properties

    + On the left of this tab you can select the Thing Type. Either select it from the list or enter the type number in the field below the list. If it is a thing type that is in the Game Configuration, the information known about that thing type is displayed as well.
    +
    + On the right you can change the Settings for the thing. The settings available depend on the Game Configuration you are using. Below that you can change the Angle. In Hexen and UDMF formats, you can also specify the Z height of the thing. The Z height is usually relative to the sector floor, but for some things the Z height is absolute (refer to the sourceport documentation). + +

    Action

    + This tab is only available in Hexen and UDMF formats. You can set the thing Tag that is used to refer to this thing. Below that is the Action area, which allows you to set a special action that is executed by the thing (for monsters this is usually when it dies). Please note that some things use the action arguments for their own properties and that setting an action on those things may give an unexpected result (refer to the sourceport documentation). + +

    Custom

    + This tab allows you to edit the custom fields on the thing. Only available in UDMF format. See Custom Fields Editor for more information. +

    +
    + diff --git a/Help/w_thingsfilters.html b/Help/w_thingsfilters.html new file mode 100644 index 0000000..ef081e4 --- /dev/null +++ b/Help/w_thingsfilters.html @@ -0,0 +1,27 @@ + + + + + Things Filters Window + + + + + + + + + + +

    Things Filters Window

    + +
    +

    + With the Things Filters dialog window, you can set up your own Things filters that you can use to show only relevant things in the editor. You can access this dialog from the View menu. Select a filter on the left to edit it, or click on New Filter to create a new one. You can remove the selected filter by clicking on the Delete Selected button. On the right is the Filter Settings are where you can edit the selected filter.
    +
    + Enter the name of your filter in the Name field. If you want to show only Things from a specific category, select the category in the Filter by category box. Below that you can select the settings by which you want to filter the Things (Filter by settings). A grayed/filled checkbox means it is not used for the filter. If the checkbox is empty, it means that the setting may not be set on the Things to pass the filter. If the checkbox is checked, it means that the setting must be set on the Things to pass the filter.
    +
    + Things Filters are stored separately for each Game Configuration. +

    +
    + diff --git a/Help/w_vertexedit.html b/Help/w_vertexedit.html new file mode 100644 index 0000000..23dce1f --- /dev/null +++ b/Help/w_vertexedit.html @@ -0,0 +1,27 @@ + + + + + Vertex Properties Window + + + + + + + + + + + +

    Vertex Properties Window

    + +
    +

    + This dialog allows you to edit the exact vertex coordinates. When a selection of multiple vertices is made, some fields may appear grayed or empty in case they are different for some of the selected elements. Setting a value in grayed or empty field will apply this to the entire selection. + +

    Custom

    + This tab allows you to edit the custom fields on the vertex. Only available in UDMF format. See Custom Fields Editor for more information. +

    +
    + diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/README.md b/README.md index 8a0c3f0..330973c 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,13 @@ -# DoomBuilder64 -Custom fork of Doom Builder 2 for DOOM 64 on PC (EX/Steam/GOG) +# Doom Builder 64 +Doom Builder 64 is a custom fork of Doom Builder 2 by CodeImp made to support mapping for DOOM 64 on PC (EX/Steam/GOG), originally developed by Samuel "Kaiser" Villarreal and now maintained by the_kovic. Doom Builder 64 currently doesn't support making maps for the original N64 release of DOOM 64. + +## Installation +Doom Builder 64 doesn't ship a traditional installer. To start using Doom Builder 64, simply download a .zip file containing the binaries from the Releases page, extract and launch. +### Requirements +Doom Builder 64 requires **Microsoft .NET Framework 3.5** installed. This should be already installed on the vast majority of computers. If your computer is an exception, consult Microsoft's pages on how to install. + +## Building +Currently, only building on **Windows** is tested and officially supported. To build Doom Builder 64, use the Visual Studio solution file provided in the repository. + +## Links +If you have any problems or suggestions, use the Issues or Pull Requests pages in this repository or join the [DOOM 64 Discord server](https://discord.gg/Ktxz8nz). diff --git a/Source/Core/Actions/Action.cs b/Source/Core/Actions/Action.cs new file mode 100644 index 0000000..66b2437 --- /dev/null +++ b/Source/Core/Actions/Action.cs @@ -0,0 +1,290 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Text; +using System.Windows.Forms; +using CodeImp.DoomBuilder.IO; + +#endregion + +namespace CodeImp.DoomBuilder.Actions +{ + public class Action + { + #region ================== Variables + + // Description + private string name; + private string shortname; + private string title; + private string description; + private string category; + + // Shortcut key + private int key; + private int keymask; + private int defaultkey; + + // Shortcut options + private bool allowkeys; + private bool allowmouse; + private bool allowscroll; + private bool disregardshift; + private bool disregardcontrol; + private bool repeat; + + // Delegate + private List begindelegates; + private List enddelegates; + + #endregion + + #region ================== Properties + + public string Name { get { return name; } } + public string ShortName { get { return shortname; } } + public string Category { get { return category; } } + public string Title { get { return title; } } + public string Description { get { return description; } } + public int ShortcutKey { get { return key; } } + public int ShortcutMask { get { return keymask; } } + public int DefaultShortcutKey { get { return defaultkey; } } + public bool AllowKeys { get { return allowkeys; } } + public bool AllowMouse { get { return allowmouse; } } + public bool AllowScroll { get { return allowscroll; } } + public bool DisregardShift { get { return disregardshift; } } + public bool DisregardControl { get { return disregardcontrol; } } + public bool Repeat { get { return repeat; } } + public bool BeginBound { get { return (begindelegates.Count > 0); } } + public bool EndBound { get { return (enddelegates.Count > 0); } } + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + internal Action(Configuration cfg, string name, string shortname, int key) + { + // Initialize + this.name = name; + this.shortname = shortname; + this.title = cfg.ReadSetting(shortname + ".title", "[" + name + "]"); + this.category = cfg.ReadSetting(shortname + ".category", ""); + this.description = cfg.ReadSetting(shortname + ".description", ""); + this.allowkeys = cfg.ReadSetting(shortname + ".allowkeys", true); + this.allowmouse = cfg.ReadSetting(shortname + ".allowmouse", true); + this.allowscroll = cfg.ReadSetting(shortname + ".allowscroll", false); + this.disregardshift = cfg.ReadSetting(shortname + ".disregardshift", false); + this.disregardcontrol = cfg.ReadSetting(shortname + ".disregardcontrol", false); + this.repeat = cfg.ReadSetting(shortname + ".repeat", false); + this.defaultkey = cfg.ReadSetting(shortname + ".default", 0); + this.begindelegates = new List(); + this.enddelegates = new List(); + + if (disregardshift) + keymask = (int)Keys.Shift; + else + keymask = 0; + + if (disregardcontrol) + keymask |= (int)Keys.Control; + + keymask = ~keymask; + + if (key == -1) + { + this.key = -1; + } + else + { + this.key = key & keymask; + } + } + + // Destructor + ~Action() + { + // Moo. + } + + #endregion + + #region ================== Static Methods + + // This returns the shortcut key description for a key + public static string GetShortcutKeyDesc(int key) + { + KeysConverter conv = new KeysConverter(); + int ctrl, button; + string ctrlprefix = ""; + + // When key is 0, then return an empty string + if (key == 0) return ""; + + // Split the key in Control and Button + ctrl = key & ((int)Keys.Control | (int)Keys.Shift | (int)Keys.Alt); + button = key & ~((int)Keys.Control | (int)Keys.Shift | (int)Keys.Alt); + + // When the button is a control key, then remove the control itsself + if ((button == (int)Keys.ControlKey) || + (button == (int)Keys.ShiftKey)) + { + ctrl = 0; + key = key & ~((int)Keys.Control | (int)Keys.Shift | (int)Keys.Alt); + } + + // Determine control prefix + if (ctrl != 0) ctrlprefix = conv.ConvertToString(key); + + // Check if button is special + switch (button) + { + // Scroll down + case (int)SpecialKeys.MScrollDown: + + // Make string representation + return ctrlprefix + "ScrollDown"; + + // Scroll up + case (int)SpecialKeys.MScrollUp: + + // Make string representation + return ctrlprefix + "ScrollUp"; + + // Keys that would otherwise have odd names + case (int)Keys.Oemtilde: return ctrlprefix + "~"; + case (int)Keys.OemMinus: return ctrlprefix + "-"; + case (int)Keys.Oemplus: return ctrlprefix + "+"; + case (int)Keys.Subtract: return ctrlprefix + "NumPad-"; + case (int)Keys.Add: return ctrlprefix + "NumPad+"; + case (int)Keys.Decimal: return ctrlprefix + "NumPad."; + case (int)Keys.Multiply: return ctrlprefix + "NumPad*"; + case (int)Keys.Divide: return ctrlprefix + "NumPad/"; + case (int)Keys.OemOpenBrackets: return ctrlprefix + "["; + case (int)Keys.OemCloseBrackets: return ctrlprefix + "]"; + case (int)Keys.Oem1: return ctrlprefix + ";"; + case (int)Keys.Oem7: return ctrlprefix + "'"; + case (int)Keys.Oemcomma: return ctrlprefix + ","; + case (int)Keys.OemPeriod: return ctrlprefix + "."; + case (int)Keys.OemQuestion: return ctrlprefix + "?"; + case (int)Keys.Oem5: return ctrlprefix + "\\"; + case (int)Keys.Capital: return ctrlprefix + "CapsLock"; + case (int)Keys.Back: return ctrlprefix + "Backspace"; + + default: + + // Use standard key-string conversion + return conv.ConvertToString(key); + } + } + + #endregion + + #region ================== Methods + + // This invokes the action + public void Invoke() + { + this.Begin(); + this.End(); + } + + // This sets a new key for the action + internal void SetShortcutKey(int key) + { + // Make it so. + this.key = key & keymask; + } + + // This binds a delegate to this action + internal void BindBegin(ActionDelegate method) + { + begindelegates.Add(method); + } + + // This removes a delegate from this action + internal void UnbindBegin(ActionDelegate method) + { + begindelegates.Remove(method); + } + + // This binds a delegate to this action + internal void BindEnd(ActionDelegate method) + { + enddelegates.Add(method); + } + + // This removes a delegate from this action + internal void UnbindEnd(ActionDelegate method) + { + enddelegates.Remove(method); + } + + // This raises events for this action + internal void Begin() + { + List delegateslist; + + // Method bound? + if (begindelegates.Count > 0) + { + // Copy delegates list + delegateslist = new List(begindelegates); + + // Invoke all the delegates + General.Actions.Current = this; + General.Actions.ResetExclusiveRequest(); + foreach (ActionDelegate ad in delegateslist) ad.Invoke(); + General.Actions.ResetExclusiveRequest(); + General.Actions.Current = null; + } + } + + // This raises events for this action + internal void End() + { + List delegateslist; + + // Method bound? + if (enddelegates.Count > 0) + { + // Copy delegates list + delegateslist = new List(enddelegates); + + // Invoke all the delegates + General.Actions.Current = this; + General.Actions.ResetExclusiveRequest(); + foreach (ActionDelegate ad in delegateslist) ad.Invoke(); + General.Actions.ResetExclusiveRequest(); + General.Actions.Current = null; + } + } + + // This checks if the action qualifies for a key combination + public bool KeyMatches(int pressedkey) + { + return (key == (pressedkey & keymask)); + } + + #endregion + } +} diff --git a/Source/Core/Actions/ActionAttribute.cs b/Source/Core/Actions/ActionAttribute.cs new file mode 100644 index 0000000..723738d --- /dev/null +++ b/Source/Core/Actions/ActionAttribute.cs @@ -0,0 +1,96 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Text; +using System.Reflection; + +#endregion + +namespace CodeImp.DoomBuilder.Actions +{ + /// + /// This binds a method to an action. + /// + [AttributeUsage(AttributeTargets.Method, Inherited = true, AllowMultiple = true)] + public class ActionAttribute : Attribute + { + #region ================== Variables + + // The action to bind to + protected string action; + protected bool baseaction; + protected string library; + + #endregion + + #region ================== Properties + + /// + /// Set to true to indicate this is a core Doom Builder action when used within a plugin. + /// + public bool BaseAction { get { return baseaction; } set { baseaction = value; } } + + /// + /// Set this to the name of the plugin library when this action is defined by another plugin. The library name is the filename without extension. + /// + public string Library { get { return library; } set { library = value; } } + + internal string ActionName { get { return action; } } + + #endregion + + #region ================== Constructor / Disposer + + /// + /// This binds a method to an action. + /// + /// The action name as defined in Actions.cfg resource. + public ActionAttribute(string action) + { + // Initialize + this.action = action; + this.baseaction = false; + this.library = ""; + } + + #endregion + + #region ================== Methods + + // This makes the proper name + public string GetFullActionName(Assembly asm) + { + string asmname; + + if (library.Length > 0) + asmname = library.ToLowerInvariant(); + else if (baseaction) + asmname = General.ThisAssembly.GetName().Name.ToLowerInvariant(); + else + asmname = asm.GetName().Name.ToLowerInvariant(); + + return asmname + "_" + action; + } + + #endregion + } +} diff --git a/Source/Core/Actions/ActionDelegate.cs b/Source/Core/Actions/ActionDelegate.cs new file mode 100644 index 0000000..bf2cc23 --- /dev/null +++ b/Source/Core/Actions/ActionDelegate.cs @@ -0,0 +1,30 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Text; + +#endregion + +namespace CodeImp.DoomBuilder.Actions +{ + internal delegate void ActionDelegate(); +} diff --git a/Source/Core/Actions/ActionManager.cs b/Source/Core/Actions/ActionManager.cs new file mode 100644 index 0000000..860fc35 --- /dev/null +++ b/Source/Core/Actions/ActionManager.cs @@ -0,0 +1,712 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Text; +using CodeImp.DoomBuilder.Properties; +using System.IO; +using CodeImp.DoomBuilder.IO; +using System.Collections; +using System.Reflection; +using System.Windows.Forms; + +#endregion + +namespace CodeImp.DoomBuilder.Actions +{ + public class ActionManager + { + #region ================== Constants + + private const string ACTIONS_RESOURCE = "Actions.cfg"; + + #endregion + + #region ================== Variables + + // Actions + private Dictionary actions; + + // Categories + private SortedDictionary categories; + + // Keys state + private int modifiers; + private List pressedkeys; + + // Begun actions + private List activeactions; + private Action currentaction; + + // Exclusive invokation + private bool exclusiverequested; + + // Disposing + private bool isdisposed = false; + + #endregion + + #region ================== Properties + + internal SortedDictionary Categories { get { return categories; } } + internal Action this[string action] { get { if (actions.ContainsKey(action)) return actions[action]; else throw new ArgumentException("There is no such action \"" + action + "\""); } } + public bool IsDisposed { get { return isdisposed; } } + internal bool ExclusiveRequested { get { return exclusiverequested; } } + + /// + /// Current executing action. This returns Null when no action is invoked. + /// + public Action Current { get { return currentaction; } internal set { currentaction = value; } } + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + internal ActionManager() + { + // Initialize + General.WriteLogLine("Starting action manager..."); + actions = new Dictionary(); + pressedkeys = new List(); + activeactions = new List(); + categories = new SortedDictionary(); + + // Load all actions in this assembly + LoadActions(General.ThisAssembly); + + // We have no destructor + GC.SuppressFinalize(this); + } + + // Disposer + internal void Dispose() + { + // Not already disposed? + if (!isdisposed) + { + // Clean up + + // Done + isdisposed = true; + } + } + + #endregion + + #region ================== Actions + + // This loads all actions from an assembly + internal void LoadActions(Assembly asm) + { + Stream actionsdata; + StreamReader actionsreader; + Configuration cfg; + string cat, name, title, desc, shortname; + bool amouse, akeys, ascroll, debugonly, noshift, repeat; + string[] resnames; + AssemblyName asmname = asm.GetName(); + + // Find a resource named Actions.cfg + resnames = asm.GetManifestResourceNames(); + foreach (string rn in resnames) + { + // Found one? + if (rn.EndsWith(ACTIONS_RESOURCE, StringComparison.InvariantCultureIgnoreCase)) + { + // Get a stream from the resource + actionsdata = asm.GetManifestResourceStream(rn); + actionsreader = new StreamReader(actionsdata, Encoding.ASCII); + + // Load configuration from stream + cfg = new Configuration(); + cfg.InputConfiguration(actionsreader.ReadToEnd()); + if (cfg.ErrorResult) + { + string errordesc = "Error in Actions configuration on line " + cfg.ErrorLine + ": " + cfg.ErrorDescription; + General.CancelAutoMapLoad(); + General.ErrorLogger.Add(ErrorType.Error, "Unable to read Actions configuration from assembly " + Path.GetFileName(asm.Location)); + General.WriteLogLine(errordesc); + General.ShowErrorMessage("Unable to read Actions configuration from assembly " + Path.GetFileName(asm.Location) + "!\n" + errordesc, MessageBoxButtons.OK); + } + else + { + // Read the categories structure + IDictionary cats = cfg.ReadSetting("categories", new Hashtable()); + foreach (DictionaryEntry c in cats) + { + // Make the category if not already added + if (!categories.ContainsKey(c.Key.ToString())) + categories.Add(c.Key.ToString(), c.Value.ToString()); + } + + // Go for all objects in the configuration + foreach (DictionaryEntry a in cfg.Root) + { + // Get action properties + shortname = a.Key.ToString(); + name = asmname.Name.ToLowerInvariant() + "_" + shortname; + debugonly = cfg.ReadSetting(a.Key + ".debugonly", false); + + // Not the categories structure? + if (shortname.ToLowerInvariant() != "categories") + { + // Check if action should be included + if (General.DebugBuild || !debugonly) + { + // Create an action + CreateAction(cfg, name, shortname); + } + } + } + } + + // Done with the resource + actionsreader.Dispose(); + actionsdata.Dispose(); + } + } + } + + // This manually creates an action + private void CreateAction(Configuration cfg, string name, string shortname) + { + // Action does not exist yet? + if (!actions.ContainsKey(name)) + { + // Read the key from configuration + int key = General.Settings.ReadSetting("shortcuts." + name, -1); + + // Create an action + actions.Add(name, new Action(cfg, name, shortname, key)); + } + else + { + // Action already exists! + General.ErrorLogger.Add(ErrorType.Warning, "Action '" + name + "' already exists. Action names must be unique."); + } + } + + // This binds all methods marked with this attribute + public void BindMethods(Type type) + { + // Bind static methods + BindMethods(null, type); + } + + // This binds all methods marked with this attribute + public void BindMethods(object obj) + { + // Bind instance methods + BindMethods(obj, obj.GetType()); + } + + // This binds all methods marked with this attribute + private void BindMethods(object obj, Type type) + { + MethodInfo[] methods; + ActionAttribute[] attrs; + ActionDelegate del; + string actionname; + + if (obj == null) + General.WriteLogLine("Binding static action methods for class " + type.Name + "..."); + else + General.WriteLogLine("Binding action methods for " + type.Name + " object..."); + + // Go for all methods on obj + methods = type.GetMethods(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static); + foreach (MethodInfo m in methods) + { + // Check if the method has this attribute + attrs = (ActionAttribute[])m.GetCustomAttributes(typeof(BeginActionAttribute), true); + + // Go for all attributes + foreach (ActionAttribute a in attrs) + { + // Create a delegate for this method + del = (ActionDelegate)Delegate.CreateDelegate(typeof(ActionDelegate), obj, m); + + // Make proper name + actionname = a.GetFullActionName(type.Assembly); + + // Bind method to action + if (Exists(actionname)) + actions[actionname].BindBegin(del); + else + throw new ArgumentException("Could not bind " + m.ReflectedType.Name + "." + m.Name + " to action \"" + actionname + "\", that action does not exist! Refer to, or edit Actions.cfg for all available application actions."); + } + + // Check if the method has this attribute + attrs = (ActionAttribute[])m.GetCustomAttributes(typeof(EndActionAttribute), true); + + // Go for all attributes + foreach (ActionAttribute a in attrs) + { + // Create a delegate for this method + del = (ActionDelegate)Delegate.CreateDelegate(typeof(ActionDelegate), obj, m); + + // Make proper name + actionname = a.GetFullActionName(type.Assembly); + + // Bind method to action + if (Exists(actionname)) + actions[actionname].BindEnd(del); + else + throw new ArgumentException("Could not bind " + m.ReflectedType.Name + "." + m.Name + " to action \"" + actionname + "\", that action does not exist. Refer to, or edit Actions.cfg for all available application actions."); + } + } + } + + // This binds a delegate manually + internal void BindBeginDelegate(Assembly asm, ActionDelegate d, BeginActionAttribute a) + { + string actionname; + + // Make proper name + actionname = a.GetFullActionName(asm); + + // Bind delegate to action + if (Exists(actionname)) + actions[actionname].BindBegin(d); + else + General.ErrorLogger.Add(ErrorType.Warning, "Could not bind delegate for " + d.Method.Name + " to action \"" + a.ActionName + "\" (" + actionname + "), that action does not exist. Refer to, or edit Actions.cfg for all available application actions."); + } + + // This binds a delegate manually + internal void BindEndDelegate(Assembly asm, ActionDelegate d, EndActionAttribute a) + { + string actionname; + + // Make proper name + actionname = a.GetFullActionName(asm); + + // Bind delegate to action + if (Exists(actionname)) + actions[actionname].BindEnd(d); + else + General.ErrorLogger.Add(ErrorType.Warning, "Could not bind delegate for " + d.Method.Name + " to action \"" + a.ActionName + "\" (" + actionname + "), that action does not exist. Refer to, or edit Actions.cfg for all available application actions."); + } + + // This unbinds all methods marked with this attribute + public void UnbindMethods(Type type) + { + // Unbind static methods + UnbindMethods(null, type); + } + + // This unbinds all methods marked with this attribute + public void UnbindMethods(object obj) + { + // Unbind instance methods + UnbindMethods(obj, obj.GetType()); + } + + // This unbinds all methods marked with this attribute + private void UnbindMethods(object obj, Type type) + { + MethodInfo[] methods; + ActionAttribute[] attrs; + ActionDelegate del; + string actionname; + + if (obj == null) + General.WriteLogLine("Unbinding static action methods for class " + type.Name + "..."); + else + General.WriteLogLine("Unbinding action methods for " + type.Name + " object..."); + + // Go for all methods on obj + methods = type.GetMethods(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static); + foreach (MethodInfo m in methods) + { + // Check if the method has this attribute + attrs = (ActionAttribute[])m.GetCustomAttributes(typeof(BeginActionAttribute), true); + + // Go for all attributes + foreach (ActionAttribute a in attrs) + { + // Create a delegate for this method + del = (ActionDelegate)Delegate.CreateDelegate(typeof(ActionDelegate), obj, m); + + // Make proper name + actionname = a.GetFullActionName(type.Assembly); + + // Unbind method from action + actions[actionname].UnbindBegin(del); + } + + // Check if the method has this attribute + attrs = (ActionAttribute[])m.GetCustomAttributes(typeof(EndActionAttribute), true); + + // Go for all attributes + foreach (ActionAttribute a in attrs) + { + // Create a delegate for this method + del = (ActionDelegate)Delegate.CreateDelegate(typeof(ActionDelegate), obj, m); + + // Make proper name + actionname = a.GetFullActionName(type.Assembly); + + // Unbind method from action + actions[actionname].UnbindEnd(del); + } + } + } + + // This unbinds a delegate manually + internal void UnbindBeginDelegate(Assembly asm, ActionDelegate d, BeginActionAttribute a) + { + string actionname; + + // Make proper name + actionname = a.GetFullActionName(asm); + + // Unbind delegate to action + actions[actionname].UnbindBegin(d); + } + + // This unbinds a delegate manually + internal void UnbindEndDelegate(Assembly asm, ActionDelegate d, EndActionAttribute a) + { + string actionname; + + // Make proper name + actionname = a.GetFullActionName(asm); + + // Unbind delegate to action + actions[actionname].UnbindEnd(d); + } + + // This checks if a given action exists + public bool Exists(string action) + { + return actions.ContainsKey(action); + } + + // This returns a list of all actions + internal Action[] GetAllActions() + { + Action[] list = new Action[actions.Count]; + actions.Values.CopyTo(list, 0); + return list; + } + + // This returns the specified action + public Action GetActionByName(string fullname) + { + return actions[fullname]; + } + + // This saves the control settings + internal void SaveSettings() + { + // Go for all actions + foreach (KeyValuePair a in actions) + { + // Write to configuration + General.Settings.WriteSetting("shortcuts." + a.Key, a.Value.ShortcutKey); + } + } + + // This invokes the Begin and End of the given action + public bool InvokeAction(string actionname) + { + if (Exists(actionname)) + { + actions[actionname].Invoke(); + return true; + } + else + { + return false; + } + } + + #endregion + + #region ================== Shortcut Keys + + // This applies default keys if they are not already in use + internal void ApplyDefaultShortcutKeys() + { + // Find actions that have no key set + foreach (KeyValuePair a in actions) + { + // Key set? + if (a.Value.ShortcutKey == -1) + { + // Check if the default key is not already used + bool keyused = false; + foreach (KeyValuePair d in actions) + { + // Check if the keys are the same + // Note that I use the mask of the source action to check if they match any combination + if ((d.Value.ShortcutKey & a.Value.ShortcutMask) == (a.Value.DefaultShortcutKey & a.Value.ShortcutMask)) + { + // No party. + keyused = true; + break; + } + } + + // Party? + if (!keyused) + { + // Apply the default key + a.Value.SetShortcutKey(a.Value.DefaultShortcutKey); + } + else + { + // No party. + a.Value.SetShortcutKey(0); + } + } + } + } + + // This checks if a given action is active + public bool CheckActionActive(Assembly asm, string actionname) + { + if (asm == null) asm = General.ThisAssembly; + + // Find active action + string fullname = asm.GetName().Name.ToLowerInvariant() + "_" + actionname; + foreach (Action a in activeactions) + { + if (a.Name == fullname) return true; + } + + // No such active action + return false; + } + + // Removes all shortcut keys + internal void RemoveShortcutKeys() + { + // Clear all keys + foreach (KeyValuePair a in actions) + a.Value.SetShortcutKey(0); + } + + // This notifies a key has been pressed + // Returns true when the key press has been absorbed + internal bool KeyPressed(int key) + { + int strippedkey = key & ~((int)Keys.Alt | (int)Keys.Shift | (int)Keys.Control); + if ((strippedkey == (int)Keys.ShiftKey) || (strippedkey == (int)Keys.ControlKey)) key = strippedkey; + bool repeat = pressedkeys.Contains(strippedkey); + + // Update pressed keys + if (!repeat) pressedkeys.Add(strippedkey); + + // Add action to active list + Action[] acts = GetActionsByKey(key); + bool absorbed = acts.Length > 0; + foreach (Action a in acts) if (!activeactions.Contains(a)) activeactions.Add(a); + + // Invoke actions + absorbed |= BeginActionByKey(key, repeat); + + return absorbed; + } + + // This notifies a key has been released + // Returns true when the key release has been absorbed + internal bool KeyReleased(int key) + { + int strippedkey = key & ~((int)Keys.Alt | (int)Keys.Shift | (int)Keys.Control); + List keepactions = new List(); + + // Update pressed keys + if (pressedkeys.Contains(strippedkey)) pressedkeys.Remove(strippedkey); + + // End actions that no longer match + return EndActiveActions(); + } + + // This releases all pressed keys + internal void ReleaseAllKeys() + { + // Clear pressed keys + pressedkeys.Clear(); + + // End actions + EndActiveActions(); + } + + // This updates the modifiers + internal void UpdateModifiers(int mods) + { + // Update modifiers + modifiers = mods; + + // End actions that no longer match + EndActiveActions(); + } + + // This will call the associated actions for a keypress + // Returns true when the key invokes any action + internal bool BeginActionByKey(int key, bool repeated) + { + bool invoked = false; + + // Get all actions for which a begin is bound + List boundactions = new List(actions.Count); + foreach (KeyValuePair a in actions) + if (a.Value.BeginBound) boundactions.Add(a.Value); + + // Go for all actions + foreach (Action a in boundactions) + { + // This action is associated with this key? + if (a.KeyMatches(key)) + { + invoked = true; + + // Allowed to repeat? + if (a.Repeat || !repeated) + { + // Invoke action + a.Begin(); + } + else + { + //General.WriteLogLine("Action \"" + a.Value.Name + "\" failed because it does not support repeating activation!"); + } + } + } + + return invoked; + } + + // This will end active actions for which the pressed keys do not match + // Returns true when actions have been ended + private bool EndActiveActions() + { + bool listchanged; + bool actionsended = false; + + do + { + // Go for all active actions + listchanged = false; + for (int i = 0; i < activeactions.Count; i++) + { + Action a = activeactions[i]; + + // Go for all pressed keys + bool stillactive = false; + foreach (int k in pressedkeys) + { + if ((k == (int)Keys.ShiftKey) || (k == (int)Keys.ControlKey)) + stillactive |= a.KeyMatches(k); + else + stillactive |= a.KeyMatches(k | modifiers); + } + + // End the action if no longer matches any of the keys + if (!stillactive) + { + actionsended = true; + activeactions.RemoveAt(i); + listchanged = true; + a.End(); + break; + } + } + } + while (listchanged); + + return actionsended; + } + + // This returns all action names for a given key + public string[] GetActionNamesByKey(int key) + { + List actionnames = new List(); + + // Go for all actions + foreach (KeyValuePair a in actions) + { + // This action is associated with this key? + if (a.Value.KeyMatches(key)) + { + // List short name + actionnames.Add(a.Value.ShortName); + } + } + + // Return result; + return actionnames.ToArray(); + } + + // This returns all action names for a given key + public Action[] GetActionsByKey(int key) + { + List actionnames = new List(); + + // Go for all actions + foreach (KeyValuePair a in actions) + { + // This action is associated with this key? + if (a.Value.KeyMatches(key)) + { + // List short name + actionnames.Add(a.Value); + } + } + + // Return result; + return actionnames.ToArray(); + } + + #endregion + + #region ================== Exclusive Invokation + + // This resets the exclusive request + internal void ResetExclusiveRequest() + { + exclusiverequested = false; + } + + /// + /// This asks for exclusive invokation of the current BeginAction or EndAction. + /// Returns true when successull, false when denied (already given to another caller) + /// + public bool RequestExclusiveInvokation() + { + if (exclusiverequested) + { + // Already given out + return false; + } + else + { + // Success + exclusiverequested = true; + return true; + } + } + + #endregion + } +} diff --git a/Source/Core/Actions/BeginActionAttribute.cs b/Source/Core/Actions/BeginActionAttribute.cs new file mode 100644 index 0000000..0177309 --- /dev/null +++ b/Source/Core/Actions/BeginActionAttribute.cs @@ -0,0 +1,43 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Text; +using System.Reflection; + +#endregion + +namespace CodeImp.DoomBuilder.Actions +{ + /// + /// This binds a method to an action which is then called when the action is started. + /// + public class BeginActionAttribute : ActionAttribute + { + /// + /// This binds a method to an action which is then called when the action is started. + /// + /// The action name as defined in Actions.cfg resource. + public BeginActionAttribute(string action) : base(action) + { + } + } +} diff --git a/Source/Core/Actions/EndActionAttribute.cs b/Source/Core/Actions/EndActionAttribute.cs new file mode 100644 index 0000000..abc0f17 --- /dev/null +++ b/Source/Core/Actions/EndActionAttribute.cs @@ -0,0 +1,43 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Text; +using System.Reflection; + +#endregion + +namespace CodeImp.DoomBuilder.Actions +{ + /// + /// This binds a method to an action which is then called when the action is stopped. + /// + public class EndActionAttribute : ActionAttribute + { + /// + /// This binds a method to an action which is then called when the action is stopped. + /// + /// The action name as defined in Actions.cfg resource. + public EndActionAttribute(string action) : base(action) + { + } + } +} diff --git a/Source/Core/Actions/KeyControl.cs b/Source/Core/Actions/KeyControl.cs new file mode 100644 index 0000000..575125a --- /dev/null +++ b/Source/Core/Actions/KeyControl.cs @@ -0,0 +1,81 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Text; +using CodeImp.DoomBuilder.Properties; +using System.IO; +using CodeImp.DoomBuilder.IO; +using System.Collections; +using System.Windows.Forms; + +#endregion + +namespace CodeImp.DoomBuilder.Actions +{ + internal struct KeyControl + { + #region ================== Variables + + public int key; + public string name; + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + public KeyControl(Keys key, string name) + { + // Initialize + this.key = (int)key; + this.name = name; + } + + // Constructor + public KeyControl(SpecialKeys key, string name) + { + // Initialize + this.key = (int)key; + this.name = name; + } + + // Constructor + public KeyControl(int key, string name) + { + // Initialize + this.key = key; + this.name = name; + } + + #endregion + + #region ================== Methods + + // Returns name + public override string ToString() + { + return name; + } + + #endregion + } +} diff --git a/Source/Core/Actions/MouseInput.cs b/Source/Core/Actions/MouseInput.cs new file mode 100644 index 0000000..b3c9b75 --- /dev/null +++ b/Source/Core/Actions/MouseInput.cs @@ -0,0 +1,152 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Text; +using CodeImp.DoomBuilder.Properties; +using System.IO; +using CodeImp.DoomBuilder.IO; +using System.Collections; +using System.Windows.Forms; +using SlimDX; +using SlimDX.DirectInput; +using CodeImp.DoomBuilder.Geometry; + +#endregion + +namespace CodeImp.DoomBuilder.Actions +{ + internal class MouseInput : IDisposable + { + #region ================== Variables + + // Mouse input + private DirectInput dinput; + private Mouse mouse; + + // Disposing + private bool isdisposed = false; + + #endregion + + #region ================== Properties + + public bool IsDisposed { get { return isdisposed; } } + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + public MouseInput(Control source) + { + // Initialize + dinput = new DirectInput(); + + // Start mouse input + mouse = new Mouse(dinput); + if (mouse == null) throw new Exception("No mouse device found."); + + // Set mouse input settings + mouse.Properties.AxisMode = DeviceAxisMode.Relative; + + // Set cooperative level + mouse.SetCooperativeLevel(source, + CooperativeLevel.Nonexclusive | CooperativeLevel.Foreground); + + // Aquire device + try { mouse.Acquire(); } + catch (Exception) { } + + // We have no destructor + GC.SuppressFinalize(this); + } + + // Disposer + public void Dispose() + { + // Not already disposed? + if (!isdisposed) + { + // Dispose + mouse.Unacquire(); + mouse.Dispose(); + dinput.Dispose(); + + // Clean up + mouse = null; + dinput = null; + + // Done + isdisposed = true; + } + } + + #endregion + + #region ================== Methods + + #endregion + + #region ================== Processing + + // This processes the input + public Vector2D Process() + { + MouseState ms; + float changex, changey; + + // Poll the device + try + { + Result result = mouse.Poll(); + if (result.IsSuccess) + { + // Get the changes since previous poll + ms = mouse.GetCurrentState(); + + // Calculate changes depending on sensitivity + changex = (float)ms.X * General.Settings.VisualMouseSensX * (float)General.Settings.MouseSpeed * 0.01f; + changey = (float)ms.Y * General.Settings.VisualMouseSensY * (float)General.Settings.MouseSpeed * 0.01f; + + // Return changes + return new Vector2D(changex, changey); + } + else + { + // Reaquire device + try { mouse.Acquire(); } + catch (Exception) { } + return new Vector2D(); + } + } + catch (DirectInputException) + { + // Reaquire device + try { mouse.Acquire(); } + catch (Exception) { } + return new Vector2D(); + } + } + + #endregion + } +} diff --git a/Source/Core/Actions/SpecialKeys.cs b/Source/Core/Actions/SpecialKeys.cs new file mode 100644 index 0000000..ce236a2 --- /dev/null +++ b/Source/Core/Actions/SpecialKeys.cs @@ -0,0 +1,34 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Text; + +#endregion + +namespace CodeImp.DoomBuilder.Actions +{ + public enum SpecialKeys : int + { + MScrollUp = 65530, + MScrollDown = 65531, + } +} diff --git a/Source/Core/Builder.csproj b/Source/Core/Builder.csproj new file mode 100644 index 0000000..8b6aeed --- /dev/null +++ b/Source/Core/Builder.csproj @@ -0,0 +1,974 @@ + + + + Debug + AnyCPU + 9.0.21022 + 2.0 + {818B3D10-F791-4C3F-9AF5-BB2D0079B63C} + WinExe + Properties + CodeImp.DoomBuilder + Builder + CodeImp.DoomBuilder.General + Resources\DB64.ico + + + Always + + + 3.5 + + + v3.5 + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + false + true + + + true + ..\..\Build\ + DEBUG;TRACE + true + full + x86 + false + prompt + + + + + ..\..\Build\ + true + true + none + x86 + false + prompt + ..\..\Build\Builder.xml + 3 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + UserControl + + + ArgumentBox.cs + + + UserControl + + + ColorControlSector.cs + + + UserControl + + + ScriptEditorControl.cs + + + Component + + + + Component + + + UserControl + + + ScriptEditorPanel.cs + + + Component + + + Component + + + Component + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Form + + + AboutForm.cs + + + Form + + + ActionBrowserForm.cs + + + UserControl + + + ActionSelectorControl.cs + + + UserControl + + + AngleControl.cs + + + Component + + + UserControl + + + CheckboxArrayControl.cs + + + UserControl + + + ButtonsNumericTextbox.cs + + + UserControl + + + ColorControl.cs + + + Form + + + BitFlagsForm.cs + + + Form + + + ConfigForm.cs + + + Form + + + CustomFieldsForm.cs + + + Form + + + EffectBrowserForm.cs + + + + UserControl + + + FieldsEditorControl.cs + + + Form + + + FlatBrowserForm.cs + + + UserControl + + + Form + + + GridSetupForm.cs + + + UserControl + + + ImageBrowserControl.cs + + + + UserControl + + + ImageSelectorControl.cs + + + + Form + + + LinedefEditForm.cs + + + UserControl + + + LinedefInfoPanel.cs + + + Form + + + MapOptionsForm.cs + + + Component + + + + Form + + + OpenMapOptionsForm.cs + + + Component + + + Form + + + PreferencesForm.cs + + + Component + + + Component + + + UserControl + + + ResourceListEditor.cs + + + Form + + + ResourceOptionsForm.cs + + + Form + + + ScriptEditorForm.cs + + + Form + + + SectorEditForm.cs + + + UserControl + + + SectorInfoPanel.cs + + + Form + + + TextEditForm.cs + + + Form + + + TextureBrowserForm.cs + + + UserControl + + + Form + + + TextureSetForm.cs + + + Form + + + ThingEditForm.cs + + + UserControl + + + ThingInfoPanel.cs + + + Form + + + ThingsFiltersForm.cs + + + UserControl + + + VertexInfoPanel.cs + + + + + + + + + + + + + + + + + + + Form + + + MainForm.cs + + + + + + + + + + + + + + + + + True + True + Resources.resx + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + False + ..\..\Build\Sharpzip.dll + + + + + 3.5 + + + + + + + + + + ColorControlSector.cs + Designer + + + Designer + AboutForm.cs + + + Designer + MainForm.cs + + + Designer + MapOptionsForm.cs + + + Designer + OpenMapOptionsForm.cs + + + Designer + ResourceOptionsForm.cs + + + Designer + ResXFileCodeGenerator + Resources.Designer.cs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Designer + ActionBrowserForm.cs + + + Designer + ActionSelectorControl.cs + + + Designer + CheckboxArrayControl.cs + + + Designer + ColorControl.cs + + + Designer + ConfigForm.cs + + + EffectBrowserForm.cs + Designer + + + Designer + FieldsEditorControl.cs + + + FlatBrowserForm.cs + Designer + + + Designer + GridSetupForm.cs + + + Designer + ImageBrowserControl.cs + + + Designer + ImageSelectorControl.cs + + + Designer + LinedefEditForm.cs + + + Designer + LinedefInfoPanel.cs + + + Designer + PreferencesForm.cs + + + Designer + ResourceListEditor.cs + + + Designer + SectorEditForm.cs + + + Designer + SectorInfoPanel.cs + + + Designer + TextureBrowserForm.cs + + + Designer + ThingInfoPanel.cs + + + Designer + VertexInfoPanel.cs + + + + + + + + + + + + + + + + + + + + + + + + UserControl + + + DockersControl.cs + + + Component + + + UserControl + + + PasteOptionsControl.cs + + + UserControl + + + ThingBrowserControl.cs + + + Component + + + + + + + + + + + + + + + + + + + + + + + Form + + + Form + + + ErrorsForm.cs + + + + Form + + + PasteOptionsForm.cs + + + Form + + + ScriptFindReplaceForm.cs + + + + + Form + + + ThingBrowserForm.cs + + + Form + + + VertexEditForm.cs + + + + + + + + + + + DockersControl.cs + Designer + + + Designer + PasteOptionsControl.cs + + + Designer + ThingBrowserControl.cs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Form + + + AngleForm.cs + + + + + + + + + + + + + + Designer + ScriptEditorPanel.cs + + + + + + + + + Designer + ScriptEditorControl.cs + + + + + + + + + + + + + + + Designer + AngleControl.cs + + + Designer + ArgumentBox.cs + + + Designer + ButtonsNumericTextbox.cs + + + Designer + AngleForm.cs + + + Designer + BitFlagsForm.cs + + + Designer + CustomFieldsForm.cs + + + DelayedForm.cs + Designer + + + Designer + ErrorsForm.cs + + + PasteOptionsForm.cs + Designer + + + Designer + ScriptEditorForm.cs + + + Designer + ScriptFindReplaceForm.cs + + + Designer + TextEditForm.cs + + + Designer + TextureSetForm.cs + + + Designer + ThingBrowserForm.cs + + + Designer + ThingEditForm.cs + + + Designer + ThingsFiltersForm.cs + + + + + + + + Designer + VertexEditForm.cs + + + + + False + .NET Framework 3.5 SP1 + true + + + + + + + + + \ No newline at end of file diff --git a/Source/Core/Compilers/AccCompiler.cs b/Source/Core/Compilers/AccCompiler.cs new file mode 100644 index 0000000..e59f6dc --- /dev/null +++ b/Source/Core/Compilers/AccCompiler.cs @@ -0,0 +1,181 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Runtime.InteropServices; +using System.Diagnostics; +using System.IO; +using CodeImp.DoomBuilder.Config; +using CodeImp.DoomBuilder.IO; +using System.Windows.Forms; +using System.Text.RegularExpressions; + +#endregion + +namespace CodeImp.DoomBuilder.Compilers +{ + internal sealed class AccCompiler : Compiler + { + #region ================== Constants + + private const string ACS_ERROR_FILE = "acs.err"; + + #endregion + + #region ================== Variables + + #endregion + + #region ================== Constructor + + // Constructor + public AccCompiler(CompilerInfo info) : base(info) + { + } + + // Disposer + public override void Dispose() + { + // Not already disposed? + if (!isdisposed) + { + // Clean up + + // Done + base.Dispose(); + } + } + + #endregion + + #region ================== Methods + + // This runs the compiler + public override bool Run() + { + ProcessStartInfo processinfo; + Process process; + TimeSpan deltatime; + int line = 0; + string sourcedir = Path.GetDirectoryName(sourcefile); + + // Create parameters + string args = this.parameters; + args = args.Replace("%FI", inputfile); + args = args.Replace("%FO", outputfile); + args = args.Replace("%FS", sourcefile); + args = args.Replace("%PT", this.tempdir.FullName); + args = args.Replace("%PS", sourcedir); + + // Setup process info + processinfo = new ProcessStartInfo(); + processinfo.Arguments = args; + processinfo.FileName = Path.Combine(this.tempdir.FullName, info.ProgramFile); + processinfo.CreateNoWindow = false; + processinfo.ErrorDialog = false; + processinfo.UseShellExecute = true; + processinfo.WindowStyle = ProcessWindowStyle.Hidden; + processinfo.WorkingDirectory = this.workingdir; + + // Output info + General.WriteLogLine("Running compiler..."); + General.WriteLogLine("Program: " + processinfo.FileName); + General.WriteLogLine("Arguments: " + processinfo.Arguments); + + try + { + // Start the compiler + process = Process.Start(processinfo); + } + catch (Exception e) + { + // Unable to start the compiler + General.ShowErrorMessage("Unable to start the compiler (" + info.Name + "). " + e.GetType().Name + ": " + e.Message, MessageBoxButtons.OK); + return false; + } + + // Wait for compiler to complete + process.WaitForExit(); + deltatime = TimeSpan.FromTicks(process.ExitTime.Ticks - process.StartTime.Ticks); + General.WriteLogLine("Compiler process has finished."); + General.WriteLogLine("Compile time: " + deltatime.TotalSeconds.ToString("########0.00") + " seconds"); + + // Now find the error file + string errfile = Path.Combine(this.workingdir, ACS_ERROR_FILE); + if (File.Exists(errfile)) + { + try + { + // Regex to find error lines + Regex errlinematcher = new Regex(":[0-9]+: ", RegexOptions.Compiled | RegexOptions.CultureInvariant); + + // Read all lines + string[] errlines = File.ReadAllLines(errfile); + while (line < errlines.Length) + { + // Check line + string linestr = errlines[line]; + Match match = errlinematcher.Match(linestr); + if (match.Success && (match.Index > 0)) + { + CompilerError err = new CompilerError(); + + // The match without spaces and semicolon is the line number + string linenr = match.Value.Replace(":", "").Trim(); + if (!int.TryParse(linenr, out err.linenumber)) + err.linenumber = CompilerError.NO_LINE_NUMBER; + else + err.linenumber--; + + // Everything before the match is the filename + err.filename = linestr.Substring(0, match.Index); + if (!Path.IsPathRooted(err.filename)) + { + // Add working directory to filename + err.filename = Path.Combine(processinfo.WorkingDirectory, err.filename); + } + + // Everything after the match is the description + err.description = linestr.Substring(match.Index + match.Length).Trim(); + + // Report the error + ReportError(err); + } + + // Next line + line++; + } + } + catch (Exception e) + { + // Error reading errors (ironic, isn't it) + ReportError(new CompilerError("Failed to retrieve compiler error report. " + e.GetType().Name + ": " + e.Message)); + } + } + + return true; + } + + #endregion + } +} + diff --git a/Source/Core/Compilers/Compiler.cs b/Source/Core/Compilers/Compiler.cs new file mode 100644 index 0000000..38e61b1 --- /dev/null +++ b/Source/Core/Compilers/Compiler.cs @@ -0,0 +1,217 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Runtime.InteropServices; +using System.Diagnostics; +using System.Reflection; +using System.IO; +using CodeImp.DoomBuilder.Config; +using CodeImp.DoomBuilder.IO; + +#endregion + +namespace CodeImp.DoomBuilder.Compilers +{ + public abstract class Compiler : IDisposable + { + #region ================== Variables + + // Parameters + protected CompilerInfo info; + protected string parameters; + protected string workingdir; + protected string sourcefile; + protected string outputfile; + protected string inputfile; + + // Files + protected DirectoryInfo tempdir; + + // Errors + private List errors; + + // Disposing + protected bool isdisposed; + + #endregion + + #region ================== Properties + + public string Parameters { get { return parameters; } set { parameters = value; } } + public string WorkingDirectory { get { return workingdir; } set { workingdir = value; } } + public string SourceFile { get { return sourcefile; } set { sourcefile = value; } } + public string InputFile { get { return inputfile; } set { inputfile = value; } } + public string OutputFile { get { return outputfile; } set { outputfile = value; } } + public string Location { get { return tempdir.FullName; } } + public bool IsDisposed { get { return isdisposed; } } + public CompilerError[] Errors { get { return errors.ToArray(); } } + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + public Compiler(CompilerInfo info) + { + // Initialize + this.info = info; + this.errors = new List(); + + General.WriteLogLine("Creating compiler '" + info.Name + "' on interface '" + this.GetType().Name + "'..."); + + // Create temporary directory + tempdir = Directory.CreateDirectory(General.MakeTempDirname()); + workingdir = tempdir.FullName; + + // Copy required files to the temp directory + General.WriteLogLine("Copying required files for compiler..."); + CopyRequiredFiles(); + } + + // Disposer + public virtual void Dispose() + { + if (!isdisposed) + { + Exception deleteerror = null; + double starttime = General.stopwatch.Elapsed.TotalMilliseconds; + + do + { + try + { + // Remove temporary directory + General.WriteLogLine("Removing temporary compiler files..."); + tempdir.Delete(true); + deleteerror = null; + } + catch (Exception e) + { + deleteerror = e; + } + + // Bail out when it takes too long + if ((General.stopwatch.Elapsed.TotalMilliseconds - starttime) > 2000) break; + } + while (deleteerror != null); + + // Report error if we have one + if (deleteerror != null) + { + General.ErrorLogger.Add(ErrorType.Error, "Unable to remove temporary compiler files. " + deleteerror.GetType().Name + ": " + deleteerror.Message); + General.WriteLogLine(deleteerror.StackTrace); + } + + // Disposed + isdisposed = true; + } + } + + #endregion + + #region ================== Methods + + // This copies all compiler files to a given destination + private void CopyRequiredFiles() + { + // Copy files + foreach (string f in info.Files) + { + string sourcefile = Path.Combine(info.Path, f); + string targetfile = Path.Combine(tempdir.FullName, f); + if (!File.Exists(sourcefile)) General.ErrorLogger.Add(ErrorType.Error, "The file '" + f + "' required by the '" + info.Name + "' compiler is missing. According to the compiler configuration in '" + info.FileName + "', the was expected to be found in the following path: " + info.Path); + File.Copy(sourcefile, targetfile, true); + } + } + + /// + /// This runs the compiler. + /// + /// Returns false when failed to start. + public virtual bool Run() { return false; } + + /// + /// Use this to report an error. + /// + protected void ReportError(CompilerError err) + { + this.errors.Add(err); + } + + // This creates a compiler by interface name + internal static Compiler Create(CompilerInfo info) + { + Compiler result; + + // Make list of assemblies to search in + List asms = General.Plugins.GetPluginAssemblies(); + asms.Add(General.ThisAssembly); + + // Constructor arguments + object[] args = new object[1]; + args[0] = info; + + try + { + // Go for all assemblies + foreach (Assembly a in asms) + { + Type[] types; + + // Find the class + if (a == General.ThisAssembly) + types = a.GetTypes(); + else + types = a.GetExportedTypes(); + + foreach (Type t in types) + { + if (t.IsSubclassOf(typeof(Compiler)) && (t.Name == info.ProgramInterface)) + { + // Create instance + result = (Compiler)a.CreateInstance(t.FullName, false, BindingFlags.Default, + null, args, CultureInfo.CurrentCulture, new object[0]); + return result; + } + } + } + } + // Catch errors + catch (TargetInvocationException e) + { + // Throw the actual exception + Debug.WriteLine(DateTime.Now.ToShortDateString() + " " + DateTime.Now.ToShortTimeString()); + Debug.WriteLine(e.InnerException.Source + " throws " + e.InnerException.GetType().Name + ":"); + Debug.WriteLine(e.InnerException.Message); + Debug.WriteLine(e.InnerException.StackTrace); + throw e.InnerException; + } + + // No such compiler + return null; + } + + #endregion + } +} + diff --git a/Source/Core/Compilers/CompilerError.cs b/Source/Core/Compilers/CompilerError.cs new file mode 100644 index 0000000..113135a --- /dev/null +++ b/Source/Core/Compilers/CompilerError.cs @@ -0,0 +1,66 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Runtime.InteropServices; +using System.Diagnostics; + +#endregion + +namespace CodeImp.DoomBuilder.Compilers +{ + public struct CompilerError + { + // Constants + public const int NO_LINE_NUMBER = -1; + + // Members + public string description; + public string filename; + public int linenumber; + + // Constructor + public CompilerError(string description) + { + this.description = description; + this.filename = ""; + this.linenumber = NO_LINE_NUMBER; + } + + // Constructor + public CompilerError(string description, string filename) + { + this.description = description; + this.filename = filename; + this.linenumber = NO_LINE_NUMBER; + } + + // Constructor + public CompilerError(string description, string filename, int linenumber) + { + this.description = description; + this.filename = filename; + this.linenumber = linenumber; + } + } +} + diff --git a/Source/Core/Compilers/MacroCompiler.cs b/Source/Core/Compilers/MacroCompiler.cs new file mode 100644 index 0000000..904f1f7 --- /dev/null +++ b/Source/Core/Compilers/MacroCompiler.cs @@ -0,0 +1,157 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Runtime.InteropServices; +using System.Diagnostics; +using System.IO; +using CodeImp.DoomBuilder.Config; +using CodeImp.DoomBuilder.IO; +using System.Windows.Forms; +using System.Text.RegularExpressions; + +#endregion + +namespace CodeImp.DoomBuilder.Compilers +{ + internal sealed class BlamCompiler : Compiler + { + #region ================== Constants + + private const string BLAM_ERROR_FILE = "blam_err.txt"; + + #endregion + + #region ================== Variables + + #endregion + + #region ================== Constructor + + // Constructor + public BlamCompiler(CompilerInfo info) : base(info) + { + } + + // Disposer + public override void Dispose() + { + // Not already disposed? + if (!isdisposed) + { + // Clean up + + // Done + base.Dispose(); + } + } + + #endregion + + #region ================== Methods + + // This runs the compiler + public override bool Run() + { + ProcessStartInfo processinfo; + Process process; + TimeSpan deltatime; + int line = 0; + string sourcedir = Path.GetDirectoryName(sourcefile); + + // Create parameters + string args = this.parameters; + args = args.Replace("%FI", inputfile); + args = args.Replace("%FO", outputfile); + args = args.Replace("%FS", sourcefile); + args = args.Replace("%PT", this.tempdir.FullName); + args = args.Replace("%PS", sourcedir); + + // Setup process info + processinfo = new ProcessStartInfo(); + processinfo.Arguments = args; + processinfo.FileName = Path.Combine(this.tempdir.FullName, info.ProgramFile); + processinfo.CreateNoWindow = false; + processinfo.ErrorDialog = false; + processinfo.UseShellExecute = true; + processinfo.WindowStyle = ProcessWindowStyle.Hidden; + processinfo.WorkingDirectory = this.workingdir; + + // Output info + General.WriteLogLine("Running compiler..."); + General.WriteLogLine("Program: " + processinfo.FileName); + General.WriteLogLine("Arguments: " + processinfo.Arguments); + + try + { + // Start the compiler + process = Process.Start(processinfo); + } + catch (Exception e) + { + // Unable to start the compiler + General.ShowErrorMessage("Unable to start the compiler (" + info.Name + "). " + e.GetType().Name + ": " + e.Message, MessageBoxButtons.OK); + return false; + } + + // Wait for compiler to complete + process.WaitForExit(); + deltatime = TimeSpan.FromTicks(process.ExitTime.Ticks - process.StartTime.Ticks); + General.WriteLogLine("Compiler process has finished."); + General.WriteLogLine("Compile time: " + deltatime.TotalSeconds.ToString("########0.00") + " seconds"); + + // Now find the error file + string errfile = Path.Combine(this.workingdir, BLAM_ERROR_FILE); + if (File.Exists(errfile)) + { + try + { + // Read all lines + string[] errlines = File.ReadAllLines(errfile); + while (line < errlines.Length) + { + // Check line + string linestr = errlines[line]; + CompilerError err = new CompilerError(); + + err.filename = inputfile; + err.description = linestr; + ReportError(err); + + // Next line + line++; + } + } + catch (Exception e) + { + // Error reading errors (ironic, isn't it) + ReportError(new CompilerError("Failed to retrieve compiler error report. " + e.GetType().Name + ": " + e.Message)); + } + } + + return true; + } + + #endregion + } +} + diff --git a/Source/Core/Compilers/NodesCompiler.cs b/Source/Core/Compilers/NodesCompiler.cs new file mode 100644 index 0000000..1772c2e --- /dev/null +++ b/Source/Core/Compilers/NodesCompiler.cs @@ -0,0 +1,125 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Runtime.InteropServices; +using System.Diagnostics; +using System.IO; +using CodeImp.DoomBuilder.Config; +using System.Windows.Forms; + +#endregion + +namespace CodeImp.DoomBuilder.Compilers +{ + internal sealed class NodesCompiler : Compiler + { + #region ================== Constants + + #endregion + + #region ================== Variables + + #endregion + + #region ================== Properties + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + public NodesCompiler(CompilerInfo info) : base(info) + { + // Initialize + + // We have no destructor + GC.SuppressFinalize(this); + } + + // Disposer + public override void Dispose() + { + // Not already disposed? + if (!isdisposed) + { + // Clean up + + // Done + base.Dispose(); + } + } + + #endregion + + #region ================== Methods + + // This runs the compiler with a file as input. + public override bool Run() + { + ProcessStartInfo processinfo; + Process process; + TimeSpan deltatime; + + // Create parameters + string args = this.parameters; + args = args.Replace("%FI", inputfile); + args = args.Replace("%FO", outputfile); + + // Setup process info + processinfo = new ProcessStartInfo(); + processinfo.Arguments = args; + processinfo.FileName = Path.Combine(this.tempdir.FullName, info.ProgramFile); + processinfo.CreateNoWindow = false; + processinfo.ErrorDialog = false; + processinfo.UseShellExecute = true; + processinfo.WindowStyle = ProcessWindowStyle.Hidden; + processinfo.WorkingDirectory = this.workingdir; + + // Output info + General.WriteLogLine("Running compiler..."); + General.WriteLogLine("Program: " + processinfo.FileName); + General.WriteLogLine("Arguments: " + processinfo.Arguments); + + try + { + // Start the compiler + process = Process.Start(processinfo); + } + catch (Exception e) + { + // Unable to start the compiler + General.ShowErrorMessage("Unable to start the compiler (" + info.Name + "). " + e.GetType().Name + ": " + e.Message, MessageBoxButtons.OK); + return false; + } + + // Wait for compiler to complete + process.WaitForExit(); + deltatime = TimeSpan.FromTicks(process.ExitTime.Ticks - process.StartTime.Ticks); + General.WriteLogLine("Compiler process has finished."); + General.WriteLogLine("Compile time: " + deltatime.TotalSeconds.ToString("########0.00") + " seconds"); + return true; + } + + #endregion + } +} diff --git a/Source/Core/Config/AllTexturesSet.cs b/Source/Core/Config/AllTexturesSet.cs new file mode 100644 index 0000000..a5861c2 --- /dev/null +++ b/Source/Core/Config/AllTexturesSet.cs @@ -0,0 +1,85 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Data; +using System.IO; +using System.Diagnostics; +using System.Windows.Forms; +using System.Text.RegularExpressions; +using System.Collections.Specialized; + +#endregion + +namespace CodeImp.DoomBuilder.Config +{ + internal sealed class AllTextureSet : TextureSet, IFilledTextureSet + { + #region ================== Constants + + public const string NAME = "All"; + + #endregion + + #region ================== Variables + + // Matching textures and flats + private List textures; + private List flats; + + #endregion + + #region ================== Properties + + public ICollection Textures { get { return textures; } } + public ICollection Flats { get { return flats; } } + + #endregion + + #region ================== Constructor / Destructor + + // New texture set constructor + public AllTextureSet() + { + this.name = NAME; + this.textures = new List(); + this.flats = new List(); + } + + #endregion + + #region ================== Methods + + internal void AddTexture(ImageData image) + { + textures.Add(image); + } + + internal void AddFlat(ImageData image) + { + flats.Add(image); + } + + #endregion + } +} diff --git a/Source/Core/Config/ArgumentInfo.cs b/Source/Core/Config/ArgumentInfo.cs new file mode 100644 index 0000000..6484073 --- /dev/null +++ b/Source/Core/Config/ArgumentInfo.cs @@ -0,0 +1,119 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Data; +using System.IO; +using System.Diagnostics; + +#endregion + +namespace CodeImp.DoomBuilder.Config +{ + public class ArgumentInfo + { + #region ================== Constants + + #endregion + + #region ================== Variables + + private string title; + private bool used; + private int type; + private EnumList enumlist; + + #endregion + + #region ================== Properties + + public string Title { get { return title; } } + public bool Used { get { return used; } } + public int Type { get { return type; } } + public EnumList Enum { get { return enumlist; } } + + #endregion + + #region ================== Constructor / Disposer + + // Constructor for argument info from configuration + internal ArgumentInfo(Configuration cfg, string argspath, int argindex, IDictionary enums) + { + // Read + string istr = argindex.ToString(CultureInfo.InvariantCulture); + this.used = cfg.SettingExists(argspath + ".arg" + istr); + this.title = cfg.ReadSetting(argspath + ".arg" + istr + ".title", "Argument " + (argindex + 1)); + this.type = cfg.ReadSetting(argspath + ".arg" + istr + ".type", 0); + + // Determine enum type + EnumList enumlist = null; + IDictionary argdic = cfg.ReadSetting(argspath + ".arg" + istr, new Hashtable()); + if (argdic.Contains("enum")) + { + // Enum fully specified? + if (argdic["enum"] is IDictionary) + { + // Create anonymous enum + this.enumlist = new EnumList(argdic["enum"] as IDictionary); + } + else + { + // Check if referenced enum exists + if ((argdic["enum"].ToString().Length > 0) && enums.ContainsKey(argdic["enum"].ToString())) + { + // Get the enum list + this.enumlist = enums[argdic["enum"].ToString()] as EnumList; + } + else + { + General.ErrorLogger.Add(ErrorType.Warning, "'" + argspath + ".arg" + istr + "' references unknown enumeration '" + argdic["enum"] + "'."); + } + } + } + } + + // Constructor for unknown argument info + internal ArgumentInfo(int argindex) + { + // Read + this.used = false; + this.title = "Argument " + (argindex + 1); + this.type = 0; + this.enumlist = new EnumList(); + } + + #endregion + + #region ================== Methods + + // This gets the description for an argument value + public string GetValueDescription(int value) + { + // TODO: Use the registered type editor to get the description! + + return value.ToString(); + } + + #endregion + } +} diff --git a/Source/Core/Config/CompilerInfo.cs b/Source/Core/Config/CompilerInfo.cs new file mode 100644 index 0000000..8c8a2f4 --- /dev/null +++ b/Source/Core/Config/CompilerInfo.cs @@ -0,0 +1,102 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Data; +using System.IO; +using System.Diagnostics; +using CodeImp.DoomBuilder.Compilers; + +#endregion + +namespace CodeImp.DoomBuilder.Config +{ + public sealed class CompilerInfo + { + #region ================== Constants + + #endregion + + #region ================== Variables + + private string filename; + private string name; + private string programfile; + private string programinterface; + private string path; + private List files; + + #endregion + + #region ================== Properties + + public string FileName { get { return filename; } } + public string Name { get { return name; } } + public string Path { get { return path; } } + public string ProgramFile { get { return programfile; } } + public string ProgramInterface { get { return programinterface; } } + public List Files { get { return files; } } + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + internal CompilerInfo(string filename, string name, string path, Configuration cfg) + { + IDictionary cfgfiles; + + General.WriteLogLine("Registered compiler configuration '" + name + "' from '" + filename + "'"); + + // Initialize + this.filename = filename; + this.path = path; + this.name = name; + this.files = new List(); + + // Read program file and interface + this.programfile = cfg.ReadSetting("compilers." + name + ".program", ""); + this.programinterface = cfg.ReadSetting("compilers." + name + ".interface", ""); + + // Make list of files required + cfgfiles = cfg.ReadSetting("compilers." + name, new Hashtable()); + foreach (DictionaryEntry de in cfgfiles) + { + if (de.Key.ToString() != "interface") + files.Add(de.Value.ToString()); + } + } + + #endregion + + #region ================== Methods + + // This creates the actual compiler interface + internal Compiler Create() + { + return Compiler.Create(this); + } + + #endregion + } +} diff --git a/Source/Core/Config/ConfigurationInfo.cs b/Source/Core/Config/ConfigurationInfo.cs new file mode 100644 index 0000000..8c27749 --- /dev/null +++ b/Source/Core/Config/ConfigurationInfo.cs @@ -0,0 +1,299 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Data; +using System.IO; +using CodeImp.DoomBuilder.Editing; +using System.Collections.Specialized; + +#endregion + +namespace CodeImp.DoomBuilder.Config +{ + internal class ConfigurationInfo : IComparable + { + #region ================== Constants + + private const string MODE_DISABLED_KEY = "disabled"; + private const string MODE_ENABLED_KEY = "enabled"; + + // The { and } are invalid key names in a configuration so this ensures this string is unique + private const string MISSING_NODEBUILDER = "{missing nodebuilder}"; + + #endregion + + #region ================== Variables + + private string name; + private string filename; + private string settingskey; + private string defaultlumpname; + private string nodebuildersave; + private string nodebuildertest; + private DataLocationList resources; + private string testprogram; + private string testparameters; + private bool testshortpaths; + private bool customparameters; + private int testskill; + private List thingsfilters; + private List texturesets; + private Dictionary editmodes; + private string startmode; + + #endregion + + #region ================== Properties + + public string Name { get { return name; } } + public string Filename { get { return filename; } } + public string DefaultLumpName { get { return defaultlumpname; } } + public string NodebuilderSave { get { return nodebuildersave; } set { nodebuildersave = value; } } + public string NodebuilderTest { get { return nodebuildertest; } set { nodebuildertest = value; } } + public DataLocationList Resources { get { return resources; } } + public string TestProgram { get { return testprogram; } set { testprogram = value; } } + public string TestParameters { get { return testparameters; } set { testparameters = value; } } + public bool TestShortPaths { get { return testshortpaths; } set { testshortpaths = value; } } + public int TestSkill { get { return testskill; } set { testskill = value; } } + public bool CustomParameters { get { return customparameters; } set { customparameters = value; } } + internal ICollection ThingsFilters { get { return thingsfilters; } } + public List TextureSets { get { return texturesets; } } + internal Dictionary EditModes { get { return editmodes; } } + public string StartMode { get { return startmode; } internal set { startmode = value; } } + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + public ConfigurationInfo(Configuration cfg, string filename) + { + // Initialize + this.filename = filename; + this.settingskey = Path.GetFileNameWithoutExtension(filename).ToLower(); + + // Load settings from game configuration + this.name = cfg.ReadSetting("game", ""); + this.defaultlumpname = cfg.ReadSetting("defaultlumpname", ""); + + // Load settings from program configuration + this.nodebuildersave = General.Settings.ReadSetting("configurations." + settingskey + ".nodebuildersave", MISSING_NODEBUILDER); + this.nodebuildertest = General.Settings.ReadSetting("configurations." + settingskey + ".nodebuildertest", MISSING_NODEBUILDER); + this.testprogram = General.Settings.ReadSetting("configurations." + settingskey + ".testprogram", ""); + this.testparameters = General.Settings.ReadSetting("configurations." + settingskey + ".testparameters", ""); + this.testshortpaths = General.Settings.ReadSetting("configurations." + settingskey + ".testshortpaths", false); + this.customparameters = General.Settings.ReadSetting("configurations." + settingskey + ".customparameters", false); + this.testskill = General.Settings.ReadSetting("configurations." + settingskey + ".testskill", 3); + this.resources = new DataLocationList(General.Settings.Config, "configurations." + settingskey + ".resources"); + this.startmode = General.Settings.ReadSetting("configurations." + settingskey + ".startmode", "VerticesMode"); + + // Make list of things filters + thingsfilters = new List(); + IDictionary cfgfilters = General.Settings.ReadSetting("configurations." + settingskey + ".thingsfilters", new Hashtable()); + foreach (DictionaryEntry de in cfgfilters) + { + thingsfilters.Add(new ThingsFilter(General.Settings.Config, "configurations." + settingskey + ".thingsfilters." + de.Key)); + } + + // Make list of texture sets + texturesets = new List(); + IDictionary sets = General.Settings.ReadSetting("configurations." + settingskey + ".texturesets", new Hashtable()); + foreach (DictionaryEntry de in sets) + { + texturesets.Add(new DefinedTextureSet(General.Settings.Config, "configurations." + settingskey + ".texturesets." + de.Key)); + } + + // Make list of edit modes + this.editmodes = new Dictionary(); + IDictionary modes = General.Settings.ReadSetting("configurations." + settingskey + ".editmodes", new Hashtable()); + foreach (DictionaryEntry de in modes) + { + if (de.Key.ToString().StartsWith(MODE_ENABLED_KEY)) + editmodes.Add(de.Value.ToString(), true); + else if (de.Key.ToString().StartsWith(MODE_DISABLED_KEY)) + editmodes.Add(de.Value.ToString(), false); + } + } + + // Constructor + private ConfigurationInfo() + { + } + + #endregion + + #region ================== Methods + + // This compares it to other ConfigurationInfo objects + public int CompareTo(ConfigurationInfo other) + { + // Compare + return name.CompareTo(other.name); + } + + // This saves the settings to program configuration + public void SaveSettings() + { + // Write to configuration + General.Settings.WriteSetting("configurations." + settingskey + ".nodebuildersave", nodebuildersave); + General.Settings.WriteSetting("configurations." + settingskey + ".nodebuildertest", nodebuildertest); + General.Settings.WriteSetting("configurations." + settingskey + ".testprogram", testprogram); + General.Settings.WriteSetting("configurations." + settingskey + ".testparameters", testparameters); + General.Settings.WriteSetting("configurations." + settingskey + ".testshortpaths", testshortpaths); + General.Settings.WriteSetting("configurations." + settingskey + ".customparameters", customparameters); + General.Settings.WriteSetting("configurations." + settingskey + ".testskill", testskill); + General.Settings.WriteSetting("configurations." + settingskey + ".startmode", startmode); + resources.WriteToConfig(General.Settings.Config, "configurations." + settingskey + ".resources"); + + // Write filters to configuration + for (int i = 0; i < thingsfilters.Count; i++) + { + thingsfilters[i].WriteSettings(General.Settings.Config, + "configurations." + settingskey + ".thingsfilters.filter" + i.ToString(CultureInfo.InvariantCulture)); + } + + // Write texturesets to configuration + for (int i = 0; i < texturesets.Count; i++) + { + texturesets[i].WriteToConfig(General.Settings.Config, + "configurations." + settingskey + ".texturesets.set" + i.ToString(CultureInfo.InvariantCulture)); + } + + // Write filters to configuration + ListDictionary modeslist = new ListDictionary(); + int index = 0; + foreach (KeyValuePair em in editmodes) + { + if (em.Value) + modeslist.Add(MODE_ENABLED_KEY + index.ToString(CultureInfo.InvariantCulture), em.Key); + else + modeslist.Add(MODE_DISABLED_KEY + index.ToString(CultureInfo.InvariantCulture), em.Key); + + index++; + } + General.Settings.WriteSetting("configurations." + settingskey + ".editmodes", modeslist); + } + + // String representation + public override string ToString() + { + return name; + } + + // This clones the object + public ConfigurationInfo Clone() + { + ConfigurationInfo ci = new ConfigurationInfo(); + ci.name = this.name; + ci.filename = this.filename; + ci.settingskey = this.settingskey; + ci.nodebuildersave = this.nodebuildersave; + ci.nodebuildertest = this.nodebuildertest; + ci.resources = new DataLocationList(); + ci.resources.AddRange(this.resources); + ci.testprogram = this.testprogram; + ci.testparameters = this.testparameters; + ci.testshortpaths = this.testshortpaths; + ci.customparameters = this.customparameters; + ci.testskill = this.testskill; + ci.startmode = this.startmode; + ci.texturesets = new List(); + foreach (DefinedTextureSet s in this.texturesets) ci.texturesets.Add(s.Copy()); + ci.thingsfilters = new List(); + foreach (ThingsFilter f in this.thingsfilters) ci.thingsfilters.Add(new ThingsFilter(f)); + ci.editmodes = new Dictionary(this.editmodes); + return ci; + } + + // This applies settings from an object + public void Apply(ConfigurationInfo ci) + { + this.name = ci.name; + this.filename = ci.filename; + this.settingskey = ci.settingskey; + this.nodebuildersave = ci.nodebuildersave; + this.nodebuildertest = ci.nodebuildertest; + this.resources = new DataLocationList(); + this.resources.AddRange(ci.resources); + this.testprogram = ci.testprogram; + this.testparameters = ci.testparameters; + this.testshortpaths = ci.testshortpaths; + this.customparameters = ci.customparameters; + this.testskill = ci.testskill; + this.startmode = ci.startmode; + this.texturesets = new List(); + foreach (DefinedTextureSet s in ci.texturesets) this.texturesets.Add(s.Copy()); + this.thingsfilters = new List(); + foreach (ThingsFilter f in ci.thingsfilters) this.thingsfilters.Add(new ThingsFilter(f)); + this.editmodes = new Dictionary(ci.editmodes); + } + + // This applies the defaults + public void ApplyDefaults(GameConfiguration gameconfig) + { + // Some of the defaults can only be applied from game configuration + if (gameconfig != null) + { + // No nodebuildes set? + if (nodebuildersave == MISSING_NODEBUILDER) nodebuildersave = gameconfig.DefaultSaveCompiler; + if (nodebuildertest == MISSING_NODEBUILDER) nodebuildertest = gameconfig.DefaultTestCompiler; + + // No texture sets? + if (texturesets.Count == 0) + { + // Copy the default texture sets from the game configuration + foreach (DefinedTextureSet s in gameconfig.TextureSets) + { + // Add a copy to our list + texturesets.Add(s.Copy()); + } + } + + // No things filters? + if (thingsfilters.Count == 0) + { + // Copy the things filters from game configuration + foreach (ThingsFilter f in gameconfig.ThingsFilters) + { + thingsfilters.Add(new ThingsFilter(f)); + } + } + } + + // Go for all available editing modes + foreach (EditModeInfo info in General.Editing.ModesInfo) + { + // Is this a mode thats is optional? + if (info.IsOptional) + { + // Add if not listed yet + if (!editmodes.ContainsKey(info.Type.FullName)) + editmodes.Add(info.Type.FullName, info.Attributes.UseByDefault); + } + } + } + + #endregion + } +} diff --git a/Source/Core/Config/DefinedTextureSet.cs b/Source/Core/Config/DefinedTextureSet.cs new file mode 100644 index 0000000..d1ba6bf --- /dev/null +++ b/Source/Core/Config/DefinedTextureSet.cs @@ -0,0 +1,110 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Data; +using System.IO; +using System.Diagnostics; +using System.Windows.Forms; +using System.Text.RegularExpressions; +using System.Collections.Specialized; + +#endregion + +namespace CodeImp.DoomBuilder.Config +{ + internal sealed class DefinedTextureSet : TextureSet + { + #region ================== Variables + + #endregion + + #region ================== Constructor / Destructor + + // Texture set from configuration + public DefinedTextureSet(Configuration cfg, string path) + { + // Read the name + name = cfg.ReadSetting(path + ".name", "Unnamed Set"); + + // Read the filters + IDictionary dic = cfg.ReadSetting(path, new Hashtable()); + filters = new List(dic.Count); + foreach (DictionaryEntry de in dic) + { + // If not the name of this texture set, add value as filter + if (de.Key.ToString() != "name") filters.Add(de.Value.ToString().ToUpperInvariant()); + } + } + + // New texture set constructor + public DefinedTextureSet(string name) + { + this.name = name; + this.filters = new List(); + } + + #endregion + + #region ================== Methods + + // This writes the texture set to configuration + internal void WriteToConfig(Configuration cfg, string path) + { + IDictionary dic; + + // Fill structure + dic = new ListDictionary(); + + // Add name + dic.Add("name", name); + + for (int i = 0; i < filters.Count; i++) + { + // Add filters + dic.Add("filter" + i.ToString(), filters[i].ToUpperInvariant()); + } + + // Write to config + cfg.WriteSetting(path, dic); + } + + // Duplication + internal DefinedTextureSet Copy() + { + // Make a copy + DefinedTextureSet s = new DefinedTextureSet(this.name); + s.filters = new List(this.filters); + return s; + } + + // This applies the filters and name of one set to this one + internal void Apply(TextureSet set) + { + this.name = set.Name; + this.filters = new List(set.Filters); + } + + #endregion + } +} diff --git a/Source/Core/Config/EnumItem.cs b/Source/Core/Config/EnumItem.cs new file mode 100644 index 0000000..d2f5a6c --- /dev/null +++ b/Source/Core/Config/EnumItem.cs @@ -0,0 +1,86 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Data; +using System.IO; +using System.Diagnostics; +using System.Windows.Forms; + +#endregion + +namespace CodeImp.DoomBuilder.Config +{ + public class EnumItem + { + #region ================== Constants + + #endregion + + #region ================== Variables + + private string value; + private string title; + + #endregion + + #region ================== Properties + + public string Value { get { return value; } } + public string Title { get { return title; } } + + #endregion + + #region ================== Constructor + + // Constructor + public EnumItem(string value, string title) + { + // Initialize + this.value = value; + this.title = title; + } + + #endregion + + #region ================== Methods + + // String representation + public override string ToString() + { + return title; + } + + // This returns the value as int + public int GetIntValue() + { + int result; + if (int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out result)) + return result; + else + return 0; + } + + #endregion + } +} diff --git a/Source/Core/Config/EnumList.cs b/Source/Core/Config/EnumList.cs new file mode 100644 index 0000000..c3135b0 --- /dev/null +++ b/Source/Core/Config/EnumList.cs @@ -0,0 +1,104 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Data; +using System.IO; +using System.Diagnostics; +using System.Windows.Forms; + +#endregion + +namespace CodeImp.DoomBuilder.Config +{ + public class EnumList : List + { + #region ================== Constants + + #endregion + + #region ================== Variables + + #endregion + + #region ================== Properties + + #endregion + + #region ================== Constructor + + // Constructor for custom list + internal EnumList() + { + } + + // Constructor to load from dictionary + internal EnumList(IDictionary dic) + { + int index; + + // Read the dictionary + foreach (DictionaryEntry de in dic) + { + // Add item + EnumItem item = new EnumItem(de.Key.ToString(), de.Value.ToString()); + base.Add(item); + } + } + + // Constructor to load from configuration + internal EnumList(string name, Configuration cfg) + { + int index; + + // Read the list from configuration + IDictionary dic = cfg.ReadSetting("enums." + name, new Hashtable()); + foreach (DictionaryEntry de in dic) + { + // Add item + EnumItem item = new EnumItem(de.Key.ToString(), de.Value.ToString()); + base.Add(item); + } + } + + #endregion + + #region ================== Methods + + // This gets an item by value + // Returns null when item could not be found + public EnumItem GetByEnumIndex(string value) + { + // Find the item + foreach (EnumItem i in this) + { + if (i.Value == value) return i; + } + + // Nothing found + return null; + } + + #endregion + } +} diff --git a/Source/Core/Config/FlagTranslation.cs b/Source/Core/Config/FlagTranslation.cs new file mode 100644 index 0000000..d5353f1 --- /dev/null +++ b/Source/Core/Config/FlagTranslation.cs @@ -0,0 +1,101 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Data; +using System.IO; +using System.Diagnostics; +using System.Windows.Forms; + +#endregion + +namespace CodeImp.DoomBuilder.Config +{ + public class FlagTranslation : IComparable + { + #region ================== Variables + + private int flag; + private List fields; + private List values; + + #endregion + + #region ================== Properties + + public int Flag { get { return flag; } } + public List Fields { get { return fields; } } + public List FieldValues { get { return values; } } + + #endregion + + #region ================== Constructor + + // Constructor + public FlagTranslation(DictionaryEntry de) + { + // Initialize + this.fields = new List(); + this.values = new List(); + + // Set the flag + if (!int.TryParse(de.Key.ToString(), out flag)) + General.ErrorLogger.Add(ErrorType.Warning, "Invalid flag translation key in configuration. The key must be numeric."); + + // Set the fields + string[] fieldstrings = de.Value.ToString().Split(','); + foreach (string f in fieldstrings) + { + string ft = f.Trim(); + if (ft.StartsWith("!")) + { + fields.Add(ft.Substring(1).Trim()); + values.Add(false); + } + else + { + fields.Add(ft); + values.Add(true); + } + } + } + + #endregion + + #region ================== Methods + + // String representation + public override string ToString() + { + return flag.ToString(); + } + + // Comparer (highest first) + public int CompareTo(FlagTranslation other) + { + return other.flag - this.flag; + } + + #endregion + } +} diff --git a/Source/Core/Config/GameConfiguration.cs b/Source/Core/Config/GameConfiguration.cs new file mode 100644 index 0000000..186251c --- /dev/null +++ b/Source/Core/Config/GameConfiguration.cs @@ -0,0 +1,1029 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Data; +using System.IO; +using System.Diagnostics; +using System.Windows.Forms; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Editing; + +#endregion + +namespace CodeImp.DoomBuilder.Config +{ + public class GameConfiguration + { + #region ================== Constants + + #endregion + + #region ================== Variables + + // Original configuration + private Configuration cfg; + + // General settings + private string configname; + private string enginename; + private float defaulttexturescale; + private float defaultflatscale; + private bool scaledtextureoffsets; + private string defaultsavecompiler; + private string defaulttestcompiler; + private string formatinterface; + private string soundlinedefflag; + private string singlesidedflag; + private string doublesidedflag; + private string impassableflag; + private string invisibleflag; // villsa + private string monsterblockflag; // villsa + private string secretflag; // villsa + private string tagonlyflag; // villsa + private string upperunpeggedflag; + private string lowerunpeggedflag; + private bool mixtexturesflats; + private bool generalizedactions; + private bool generalizedeffects; + private int start3dmodethingtype; + private int linedefactivationsfilter; + private string testparameters; + private bool testshortpaths; + private string makedoortrack; + private int makedooraction; + private int[] makedoorargs; + private bool linetagindicatesectors; + private string decorategames; + private string skyflatname; + private int maxtexturenamelength; + private int leftboundary; + private int rightboundary; + private int topboundary; + private int bottomboundary; + private bool doomlightlevels; + + // Skills + private List skills; + + // [villsa] Texture Index + private List d64textureindex; + + // villsa - palettes + private List thingpalettes; + + // Map lumps + private IDictionary maplumpnames; // This is old, we should use maplumps instead + private Dictionary maplumps; + + // Texture/flat sources + private IDictionary textureranges; + private IDictionary flatranges; + private IDictionary patchranges; + private IDictionary spriteranges; + private IDictionary colormapranges; + + // Things + private List defaultthingflags; + private Dictionary thingflags; + private List thingcategories; + private Dictionary things; + private List thingflagstranslation; + private Dictionary thingflagscompare; //mxd + + // Linedefs + private Dictionary linedefflags; + private List sortedlinedefflags; + private Dictionary linedefactions; + private List sortedlinedefactions; + private List actioncategories; + private List linedefactivates; + private List genactioncategories; + private List linedefflagstranslation; + + // Sectors + private Dictionary sectorflags; // villsa + private List sortedsectorflags; // villsa + private List sectorflagstranslation; // villsa + private Dictionary sectoreffects; + private List sortedsectoreffects; + private List geneffectoptions; + private StepsList brightnesslevels; + + // Universal fields + private List linedeffields; + private List sectorfields; + private List sidedeffields; + private List thingfields; + private List vertexfields; + + // Enums + private Dictionary enums; + + // Defaults + private List texturesets; + private List thingfilters; + + #endregion + + #region ================== Properties + + // General settings + public string Name { get { return configname; } } + public string EngineName { get { return enginename; } } + public string DefaultSaveCompiler { get { return defaultsavecompiler; } } + public string DefaultTestCompiler { get { return defaulttestcompiler; } } + public float DefaultTextureScale { get { return defaulttexturescale; } } + public float DefaultFlatScale { get { return defaultflatscale; } } + public bool ScaledTextureOffsets { get { return scaledtextureoffsets; } } + public string FormatInterface { get { return formatinterface; } } + public string SoundLinedefFlag { get { return soundlinedefflag; } } + public string SingleSidedFlag { get { return singlesidedflag; } } + public string DoubleSidedFlag { get { return doublesidedflag; } } + public string ImpassableFlag { get { return impassableflag; } } + public string InvisibleFlag { get { return invisibleflag; } } // villsa + public string MonsterblockFlag { get { return monsterblockflag; } } // villsa + public string SecretFlag { get { return secretflag; } } // villsa + public string TagonlyFlag { get { return tagonlyflag; } } // villsa + public string UpperUnpeggedFlag { get { return upperunpeggedflag; } } + public string LowerUnpeggedFlag { get { return lowerunpeggedflag; } } + public bool MixTexturesFlats { get { return mixtexturesflats; } } + public bool GeneralizedActions { get { return generalizedactions; } } + public bool GeneralizedEffects { get { return generalizedeffects; } } + public int Start3DModeThingType { get { return start3dmodethingtype; } } + public int LinedefActivationsFilter { get { return linedefactivationsfilter; } } + public string TestParameters { get { return testparameters; } } + public bool TestShortPaths { get { return testshortpaths; } } + public string MakeDoorTrack { get { return makedoortrack; } } + public int MakeDoorAction { get { return makedooraction; } } + public int[] MakeDoorArgs { get { return makedoorargs; } } + public bool LineTagIndicatesSectors { get { return linetagindicatesectors; } } + public string DecorateGames { get { return decorategames; } } + public string SkyFlatName { get { return skyflatname; } } + public int MaxTextureNamelength { get { return maxtexturenamelength; } } + public int LeftBoundary { get { return leftboundary; } } + public int RightBoundary { get { return rightboundary; } } + public int TopBoundary { get { return topboundary; } } + public int BottomBoundary { get { return bottomboundary; } } + public bool DoomLightLevels { get { return doomlightlevels; } } + + // Skills + public List Skills { get { return skills; } } + + // [Villsa] Texture Index + public List D64TextureIndex { get { return d64textureindex; } } + + // villsa thing palettes + public List ThingPalettes { get { return thingpalettes; } } + + // Map lumps + public IDictionary MapLumpNames { get { return maplumpnames; } } + public Dictionary MapLumps { get { return maplumps; } } + + // Texture/flat sources + public IDictionary TextureRanges { get { return textureranges; } } + public IDictionary FlatRanges { get { return flatranges; } } + public IDictionary PatchRanges { get { return patchranges; } } + public IDictionary SpriteRanges { get { return spriteranges; } } + public IDictionary ColormapRanges { get { return colormapranges; } } + + // Things + public ICollection DefaultThingFlags { get { return defaultthingflags; } } + public IDictionary ThingFlags { get { return thingflags; } } + public List ThingFlagsTranslation { get { return thingflagstranslation; } } + public Dictionary ThingFlagsCompare { get { return thingflagscompare; } } //mxd + + // Linedefs + public IDictionary LinedefFlags { get { return linedefflags; } } + public List SortedLinedefFlags { get { return sortedlinedefflags; } } + public IDictionary LinedefActions { get { return linedefactions; } } + public List SortedLinedefActions { get { return sortedlinedefactions; } } + public List ActionCategories { get { return actioncategories; } } + public List LinedefActivates { get { return linedefactivates; } } + public List GenActionCategories { get { return genactioncategories; } } + public List LinedefFlagsTranslation { get { return linedefflagstranslation; } } + + // Sectors + public IDictionary SectorFlags { get { return sectorflags; } } // villsa + public List SortedSectorFlags { get { return sortedsectorflags; } } // villsa + public List SectorFlagsTranslation { get { return sectorflagstranslation; } } // villsa + public IDictionary SectorEffects { get { return sectoreffects; } } + public List SortedSectorEffects { get { return sortedsectoreffects; } } + public List GenEffectOptions { get { return geneffectoptions; } } + public StepsList BrightnessLevels { get { return brightnesslevels; } } + + // Universal fields + public List LinedefFields { get { return linedeffields; } } + public List SectorFields { get { return sectorfields; } } + public List SidedefFields { get { return sidedeffields; } } + public List ThingFields { get { return thingfields; } } + public List VertexFields { get { return vertexfields; } } + + // Enums + public IDictionary Enums { get { return enums; } } + + // Defaults + internal List TextureSets { get { return texturesets; } } + public List ThingsFilters { get { return thingfilters; } } + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + internal GameConfiguration(Configuration cfg) + { + object obj; + + // Initialize + this.cfg = cfg; + this.thingflags = new Dictionary(); + this.defaultthingflags = new List(); + this.thingcategories = new List(); + this.things = new Dictionary(); + this.linedefflags = new Dictionary(); + this.sortedlinedefflags = new List(); + this.linedefactions = new Dictionary(); + this.actioncategories = new List(); + this.sortedlinedefactions = new List(); + this.linedefactivates = new List(); + this.genactioncategories = new List(); + this.sectoreffects = new Dictionary(); + this.sortedsectoreffects = new List(); + this.geneffectoptions = new List(); + this.enums = new Dictionary(); + this.skills = new List(); + this.d64textureindex = new List(); // villsa + this.thingpalettes = new List(); // villsa + this.texturesets = new List(); + this.makedoorargs = new int[Linedef.NUM_ARGS]; + this.maplumps = new Dictionary(); + this.thingflagstranslation = new List(); + this.linedefflagstranslation = new List(); + this.thingfilters = new List(); + this.thingflagscompare = new Dictionary(); //mxd + this.brightnesslevels = new StepsList(); + this.sectorflags = new Dictionary(); // villsa + this.sortedsectorflags = new List(); // villsa + this.sectorflagstranslation = new List(); // villsa + + // Read general settings + configname = cfg.ReadSetting("game", ""); + enginename = cfg.ReadSetting("engine", ""); + defaultsavecompiler = cfg.ReadSetting("defaultsavecompiler", ""); + defaulttestcompiler = cfg.ReadSetting("defaulttestcompiler", ""); + defaulttexturescale = cfg.ReadSetting("defaulttexturescale", 1f); + defaultflatscale = cfg.ReadSetting("defaultflatscale", 1f); + scaledtextureoffsets = cfg.ReadSetting("scaledtextureoffsets", true); + formatinterface = cfg.ReadSetting("formatinterface", ""); + mixtexturesflats = cfg.ReadSetting("mixtexturesflats", false); + generalizedactions = cfg.ReadSetting("generalizedlinedefs", false); + generalizedeffects = cfg.ReadSetting("generalizedsectors", false); + start3dmodethingtype = cfg.ReadSetting("start3dmode", 0); + linedefactivationsfilter = cfg.ReadSetting("linedefactivationsfilter", 0); + testparameters = cfg.ReadSetting("testparameters", ""); + testshortpaths = cfg.ReadSetting("testshortpaths", false); + makedoortrack = cfg.ReadSetting("makedoortrack", "-"); + makedooraction = cfg.ReadSetting("makedooraction", 0); + linetagindicatesectors = cfg.ReadSetting("linetagindicatesectors", false); + decorategames = cfg.ReadSetting("decorategames", ""); + skyflatname = cfg.ReadSetting("skyflatname", "F_SKY1"); + maxtexturenamelength = cfg.ReadSetting("maxtexturenamelength", 8); + leftboundary = cfg.ReadSetting("leftboundary", -32768); + rightboundary = cfg.ReadSetting("rightboundary", 32767); + topboundary = cfg.ReadSetting("topboundary", 32767); + bottomboundary = cfg.ReadSetting("bottomboundary", -32768); + doomlightlevels = cfg.ReadSetting("doomlightlevels", true); + for (int i = 0; i < Linedef.NUM_ARGS; i++) makedoorargs[i] = cfg.ReadSetting("makedoorarg" + i.ToString(CultureInfo.InvariantCulture), 0); + + // Flags have special (invariant culture) conversion + // because they are allowed to be written as integers in the configs + obj = cfg.ReadSettingObject("invisibleflag", 0); // villsa + if (obj is int) invisibleflag = ((int)obj).ToString(CultureInfo.InvariantCulture); else invisibleflag = obj.ToString(); // villsa + obj = cfg.ReadSettingObject("blockmonsterflag", 0); // villsa + if (obj is int) monsterblockflag = ((int)obj).ToString(CultureInfo.InvariantCulture); else monsterblockflag = obj.ToString(); // villsa + obj = cfg.ReadSettingObject("secretflag", 0); // villsa + if (obj is int) secretflag = ((int)obj).ToString(CultureInfo.InvariantCulture); else secretflag = obj.ToString(); // villsa + obj = cfg.ReadSettingObject("invisibleflag", 0); // villsa + if (obj is int) invisibleflag = ((int)obj).ToString(CultureInfo.InvariantCulture); else invisibleflag = obj.ToString(); // villsa + + obj = cfg.ReadSettingObject("soundlinedefflag", 0); + if (obj is int) soundlinedefflag = ((int)obj).ToString(CultureInfo.InvariantCulture); else soundlinedefflag = obj.ToString(); + obj = cfg.ReadSettingObject("singlesidedflag", 0); + if (obj is int) singlesidedflag = ((int)obj).ToString(CultureInfo.InvariantCulture); else singlesidedflag = obj.ToString(); + obj = cfg.ReadSettingObject("doublesidedflag", 0); + if (obj is int) doublesidedflag = ((int)obj).ToString(CultureInfo.InvariantCulture); else doublesidedflag = obj.ToString(); + obj = cfg.ReadSettingObject("impassableflag", 0); + if (obj is int) impassableflag = ((int)obj).ToString(CultureInfo.InvariantCulture); else impassableflag = obj.ToString(); + obj = cfg.ReadSettingObject("upperunpeggedflag", 0); + if (obj is int) upperunpeggedflag = ((int)obj).ToString(CultureInfo.InvariantCulture); else upperunpeggedflag = obj.ToString(); + obj = cfg.ReadSettingObject("lowerunpeggedflag", 0); + if (obj is int) lowerunpeggedflag = ((int)obj).ToString(CultureInfo.InvariantCulture); else lowerunpeggedflag = obj.ToString(); + + // Get map lumps + maplumpnames = cfg.ReadSetting("maplumpnames", new Hashtable()); + + // Get texture and flat sources + textureranges = cfg.ReadSetting("textures", new Hashtable()); + flatranges = cfg.ReadSetting("flats", new Hashtable()); + patchranges = cfg.ReadSetting("patches", new Hashtable()); + spriteranges = cfg.ReadSetting("sprites", new Hashtable()); + colormapranges = cfg.ReadSetting("colormaps", new Hashtable()); + + // Map lumps + LoadMapLumps(); + + // Skills + LoadSkills(); + + // [Villsa] TextureIndex + LoadTextureIndex(); + + // villsa - ThingPalette + LoadThingPalettes(); + + // Enums + LoadEnums(); + + // Things + LoadThingFlags(); + LoadDefaultThingFlags(); + LoadThingCategories(); + + // Linedefs + LoadLinedefFlags(); + LoadLinedefActions(); + LoadLinedefActivations(); + LoadLinedefGeneralizedActions(); + + // Sectors + LoadSectorFlags(); // villsa + LoadBrightnessLevels(); + LoadSectorEffects(); + LoadSectorGeneralizedEffects(); + + // Universal fields + linedeffields = LoadUniversalFields("linedef"); + sectorfields = LoadUniversalFields("sector"); + sidedeffields = LoadUniversalFields("sidedef"); + thingfields = LoadUniversalFields("thing"); + vertexfields = LoadUniversalFields("vertex"); + + // Defaults + LoadTextureSets(); + LoadThingFilters(); + } + + // Destructor + ~GameConfiguration() + { + foreach (ThingCategory tc in thingcategories) tc.Dispose(); + foreach (LinedefActionCategory ac in actioncategories) ac.Dispose(); + } + + #endregion + + #region ================== Loading + + // This loads the map lumps + private void LoadMapLumps() + { + IDictionary dic; + + // Get map lumps list + dic = cfg.ReadSetting("maplumpnames", new Hashtable()); + foreach (DictionaryEntry de in dic) + { + // Make map lumps + MapLumpInfo lumpinfo = new MapLumpInfo(de.Key.ToString(), cfg); + maplumps.Add(de.Key.ToString(), lumpinfo); + } + } + + // This loads the enumerations + private void LoadEnums() + { + IDictionary dic; + + // Get enums list + dic = cfg.ReadSetting("enums", new Hashtable()); + foreach (DictionaryEntry de in dic) + { + // Make new enum + EnumList list = new EnumList(de.Key.ToString(), cfg); + enums.Add(de.Key.ToString(), list); + } + } + + // This loads a universal fields list + private List LoadUniversalFields(string elementname) + { + List list = new List(); + UniversalFieldInfo uf; + IDictionary dic; + + // Get fields + dic = cfg.ReadSetting("universalfields." + elementname, new Hashtable()); + foreach (DictionaryEntry de in dic) + { + try + { + // Read the field info and add to list + uf = new UniversalFieldInfo(elementname, de.Key.ToString(), cfg, enums); + list.Add(uf); + } + catch (Exception) + { + General.ErrorLogger.Add(ErrorType.Warning, "Unable to read universal field definition 'universalfields." + elementname + "." + de.Key + "' from game configuration '" + this.Name + "'"); + } + } + + // Return result + return list; + } + + // Things and thing categories + private void LoadThingCategories() + { + IDictionary dic; + ThingCategory thingcat; + + // Get thing categories + dic = cfg.ReadSetting("thingtypes", new Hashtable()); + foreach (DictionaryEntry de in dic) + { + if (de.Value is IDictionary) + { + // Make a category + thingcat = new ThingCategory(cfg, de.Key.ToString(), enums); + + // Add all things in category to the big list + foreach (ThingTypeInfo t in thingcat.Things) + { + if (!things.ContainsKey(t.Index)) + { + things.Add(t.Index, t); + } + else + { + General.ErrorLogger.Add(ErrorType.Warning, "Thing number " + t.Index + " is defined more than once (as '" + things[t.Index].Title + "' and '" + t.Title + "') in game configuration '" + this.Name + "'"); + } + } + + // Add category to list + thingcategories.Add(thingcat); + } + } + } + + // Linedef flags + private void LoadLinedefFlags() + { + IDictionary dic; + + // Get linedef flags + dic = cfg.ReadSetting("linedefflags", new Hashtable()); + foreach (DictionaryEntry de in dic) + linedefflags.Add(de.Key.ToString(), de.Value.ToString()); + + // Get translations + dic = cfg.ReadSetting("linedefflagstranslation", new Hashtable()); + foreach (DictionaryEntry de in dic) + linedefflagstranslation.Add(new FlagTranslation(de)); + + // Sort flags? + MapSetIO io = MapSetIO.Create(formatinterface); + if (io.HasNumericLinedefFlags) + { + // Make list for integers that we can sort + // villsa - change from int to uint for larger flag values (doom64) + List sortlist = new List(linedefflags.Count); + foreach (KeyValuePair f in linedefflags) + { + uint num; + if (uint.TryParse(f.Key, NumberStyles.Integer, CultureInfo.InvariantCulture, out num)) sortlist.Add(num); + } + + // Sort + sortlist.Sort(); + + // Make list of strings + foreach (uint i in sortlist) + sortedlinedefflags.Add(i.ToString(CultureInfo.InvariantCulture)); + } + + // Sort the flags, because they must be compared highest first! + linedefflagstranslation.Sort(); + } + + // Linedef actions and action categories + private void LoadLinedefActions() + { + Dictionary cats = new Dictionary(); + IDictionary dic; + LinedefActionInfo ai; + LinedefActionCategory ac; + int actionnumber; + + // Get linedef categories + dic = cfg.ReadSetting("linedeftypes", new Hashtable()); + foreach (DictionaryEntry cde in dic) + { + if (cde.Value is IDictionary) + { + // Read category title + string cattitle = cfg.ReadSetting("linedeftypes." + cde.Key + ".title", ""); + + // Make or get category + if (cats.ContainsKey(cde.Key.ToString())) + ac = cats[cde.Key.ToString()]; + else + { + ac = new LinedefActionCategory(cde.Key.ToString(), cattitle); + cats.Add(cde.Key.ToString(), ac); + } + + // Go for all line types in category + IDictionary catdic = cfg.ReadSetting("linedeftypes." + cde.Key, new Hashtable()); + foreach (DictionaryEntry de in catdic) + { + // Check if the item key is numeric + if (int.TryParse(de.Key.ToString(), + NumberStyles.AllowLeadingWhite | NumberStyles.AllowTrailingWhite, + CultureInfo.InvariantCulture, out actionnumber)) + { + // Check if the item value is a structure + if (de.Value is IDictionary) + { + // Make the line type + ai = new LinedefActionInfo(actionnumber, cfg, cde.Key.ToString(), enums); + + // Add action to category and sorted list + sortedlinedefactions.Add(ai); + linedefactions.Add(actionnumber, ai); + ac.Add(ai); + } + else + { + // Failure + if (de.Value != null) + General.ErrorLogger.Add(ErrorType.Warning, "Structure 'linedeftypes' contains invalid types in game configuration '" + this.Name + "'. All types must be expanded structures."); + } + } + } + } + } + + // Sort the actions list + sortedlinedefactions.Sort(); + + // Copy categories to final list + actioncategories.Clear(); + actioncategories.AddRange(cats.Values); + + // Sort the categories list + actioncategories.Sort(); + } + + // Linedef activates + private void LoadLinedefActivations() + { + IDictionary dic; + int bitvalue; + + // Get linedef activations + dic = cfg.ReadSetting("linedefactivations", new Hashtable()); + foreach (DictionaryEntry de in dic) + { + // Add to the list + linedefactivates.Add(new LinedefActivateInfo(de.Key.ToString(), de.Value.ToString())); + } + + // Sort the list + linedefactivates.Sort(); + } + + // Linedef generalized actions + private void LoadLinedefGeneralizedActions() + { + IDictionary dic; + + // Get linedef activations + dic = cfg.ReadSetting("gen_linedeftypes", new Hashtable()); + foreach (DictionaryEntry de in dic) + { + // Check for valid structure + if (de.Value is IDictionary) + { + // Add category + genactioncategories.Add(new GeneralizedCategory("gen_linedeftypes", de.Key.ToString(), cfg)); + } + else + { + General.ErrorLogger.Add(ErrorType.Warning, "Structure 'gen_linedeftypes' contains invalid entries in game configuration '" + this.Name + "'"); + } + } + } + + // villsa - sector flags + private void LoadSectorFlags() + { + IDictionary dic; + + // Get sector flags + dic = cfg.ReadSetting("sectorflags", new Hashtable()); + foreach (DictionaryEntry de in dic) + sectorflags.Add(de.Key.ToString(), de.Value.ToString()); + + // Get translations + dic = cfg.ReadSetting("sectorflagstranslation", new Hashtable()); + foreach (DictionaryEntry de in dic) + sectorflagstranslation.Add(new FlagTranslation(de)); + + // Sort flags? + MapSetIO io = MapSetIO.Create(formatinterface); + + // Make list for integers that we can sort + List sortlist = new List(sectorflags.Count); + foreach (KeyValuePair f in sectorflags) + { + int num; + if (int.TryParse(f.Key, NumberStyles.Integer, CultureInfo.InvariantCulture, out num)) sortlist.Add(num); + } + + // Sort + sortlist.Sort(); + + // Make list of strings + foreach (int i in sortlist) + sortedsectorflags.Add(i.ToString(CultureInfo.InvariantCulture)); + + // Sort the flags, because they must be compared highest first! + sectorflagstranslation.Sort(); + } + + // Sector effects + private void LoadSectorEffects() + { + IDictionary dic; + SectorEffectInfo si; + int actionnumber; + + // Get sector effects + dic = cfg.ReadSetting("sectortypes", new Hashtable()); + foreach (DictionaryEntry de in dic) + { + // Try paring the action number + if (int.TryParse(de.Key.ToString(), + NumberStyles.AllowLeadingWhite | NumberStyles.AllowTrailingWhite, + CultureInfo.InvariantCulture, out actionnumber)) + { + // Make effects + si = new SectorEffectInfo(actionnumber, de.Value.ToString(), true, false); + + // Add action to category and sorted list + sortedsectoreffects.Add(si); + sectoreffects.Add(actionnumber, si); + } + else + { + General.ErrorLogger.Add(ErrorType.Warning, "Structure 'sectortypes' contains invalid keys in game configuration '" + this.Name + "'"); + } + } + + // Sort the actions list + sortedsectoreffects.Sort(); + } + + // Brightness levels + private void LoadBrightnessLevels() + { + IDictionary dic; + int level; + + // Get brightness levels structure + dic = cfg.ReadSetting("sectorbrightness", new Hashtable()); + foreach (DictionaryEntry de in dic) + { + // Try paring the level + if (int.TryParse(de.Key.ToString(), + NumberStyles.AllowLeadingWhite | NumberStyles.AllowTrailingWhite, + CultureInfo.InvariantCulture, out level)) + { + brightnesslevels.Add(level); + } + else + { + General.ErrorLogger.Add(ErrorType.Warning, "Structure 'sectorbrightness' contains invalid keys in game configuration '" + this.Name + "'"); + } + } + + // Sort the list + brightnesslevels.Sort(); + } + + // Sector generalized effects + private void LoadSectorGeneralizedEffects() + { + IDictionary dic; + + // Get sector effects + dic = cfg.ReadSetting("gen_sectortypes", new Hashtable()); + foreach (DictionaryEntry de in dic) + { + // Check for valid structure + if (de.Value is IDictionary) + { + // Add option + geneffectoptions.Add(new GeneralizedOption("gen_sectortypes", "", de.Key.ToString(), de.Value as IDictionary)); + } + else + { + General.ErrorLogger.Add(ErrorType.Warning, "Structure 'gen_sectortypes' contains invalid entries in game configuration '" + this.Name + "'"); + } + } + } + + // Thing flags + private void LoadThingFlags() + { + IDictionary dic; + + // Get linedef flags + dic = cfg.ReadSetting("thingflags", new Hashtable()); + foreach (DictionaryEntry de in dic) + thingflags.Add(de.Key.ToString(), de.Value.ToString()); + + // Get thing compare flag info (for the stuck thing error checker + HashSet flagscache = new HashSet(); + dic = cfg.ReadSetting("thingflagscompare", new Hashtable()); + foreach (DictionaryEntry de in dic) + { + string group = de.Key.ToString(); //mxd + thingflagscompare[group] = new ThingFlagsCompareGroup(cfg, group); //mxd + foreach (string s in thingflagscompare[group].Flags.Keys) + { + if (flagscache.Contains(s)) + General.ErrorLogger.Add(ErrorType.Warning, "ThingFlagsCompare flag \"" + s + "\" is double defined in the \"" + group + "\" group of the \"" + this.Name + "\" game configuration"); + else + flagscache.Add(s); + } + } + + // Get translations + dic = cfg.ReadSetting("thingflagstranslation", new Hashtable()); + foreach (DictionaryEntry de in dic) + thingflagstranslation.Add(new FlagTranslation(de)); + + // Sort the translation flags, because they must be compared highest first! + thingflagstranslation.Sort(); + } + + // Default thing flags + private void LoadDefaultThingFlags() + { + IDictionary dic; + + // Get linedef flags + dic = cfg.ReadSetting("defaultthingflags", new Hashtable()); + foreach (DictionaryEntry de in dic) + { + // Check if flag exists + if (thingflags.ContainsKey(de.Key.ToString())) + { + defaultthingflags.Add(de.Key.ToString()); + } + else + { + General.ErrorLogger.Add(ErrorType.Warning, "Structure 'defaultthingflags' contains unknown thing flags in game configuration '" + this.Name + "'"); + } + } + } + + // Skills + private void LoadSkills() + { + IDictionary dic; + + // Get skills + dic = cfg.ReadSetting("skills", new Hashtable()); + foreach (DictionaryEntry de in dic) + { + int num = 0; + if (int.TryParse(de.Key.ToString(), out num)) + { + skills.Add(new SkillInfo(num, de.Value.ToString())); + } + else + { + General.ErrorLogger.Add(ErrorType.Warning, "Structure 'skills' contains invalid skill numbers in game configuration '" + this.Name + "'"); + } + } + } + + // [Villsa] TextureIndex + private void LoadTextureIndex() + { + IDictionary dic; + + dic = cfg.ReadSetting("textureindex", new Hashtable()); + foreach (DictionaryEntry de in dic) + { + int num = 0; + if (int.TryParse(de.Key.ToString(), out num)) + { + d64textureindex.Add(new TextureIndexInfo(num, de.Value.ToString())); + } + else + { + General.ErrorLogger.Add(ErrorType.Warning, "Structure 'textureindex' contains invalid texture numbers in game configuration '" + this.Name + "'"); + } + } + } + + // villsa - Load Thing Palette info + private void LoadThingPalettes() + { + IDictionary dic; + + dic = cfg.ReadSetting("thingpalettes", new Hashtable()); + foreach (DictionaryEntry de in dic) + { + int num = 0; + if (int.TryParse(de.Key.ToString(), out num)) + { + thingpalettes.Add(new TextureIndexInfo(num, de.Value.ToString())); + } + else + { + General.ErrorLogger.Add(ErrorType.Warning, "Structure 'thingpalettes' contains invalid numbers in game configuration '" + this.Name + "'"); + } + } + } + + // Texture Sets + private void LoadTextureSets() + { + IDictionary dic; + + // Get sets + dic = cfg.ReadSetting("texturesets", new Hashtable()); + foreach (DictionaryEntry de in dic) + { + DefinedTextureSet s = new DefinedTextureSet(cfg, "texturesets." + de.Key.ToString()); + texturesets.Add(s); + } + } + + // Thing Filters + private void LoadThingFilters() + { + IDictionary dic; + + // Get sets + dic = cfg.ReadSetting("thingsfilters", new Hashtable()); + foreach (DictionaryEntry de in dic) + { + ThingsFilter f = new ThingsFilter(cfg, "thingsfilters." + de.Key.ToString()); + thingfilters.Add(f); + } + } + + #endregion + + #region ================== Methods + + // ReadSetting + public string ReadSetting(string setting, string defaultsetting) { return cfg.ReadSetting(setting, defaultsetting); } + public int ReadSetting(string setting, int defaultsetting) { return cfg.ReadSetting(setting, defaultsetting); } + public float ReadSetting(string setting, float defaultsetting) { return cfg.ReadSetting(setting, defaultsetting); } + public short ReadSetting(string setting, short defaultsetting) { return cfg.ReadSetting(setting, defaultsetting); } + public long ReadSetting(string setting, long defaultsetting) { return cfg.ReadSetting(setting, defaultsetting); } + public bool ReadSetting(string setting, bool defaultsetting) { return cfg.ReadSetting(setting, defaultsetting); } + public byte ReadSetting(string setting, byte defaultsetting) { return cfg.ReadSetting(setting, defaultsetting); } + public IDictionary ReadSetting(string setting, IDictionary defaultsetting) { return cfg.ReadSetting(setting, defaultsetting); } + + // This gets a list of things categories + internal List GetThingCategories() + { + return new List(thingcategories); + } + + // This gets a list of things + internal Dictionary GetThingTypes() + { + return new Dictionary(things); + } + + // This checks if an action is generalized or predefined + public static bool IsGeneralized(int action, List categories) + { + // Only actions above 0 + if (action > 0) + { + // Go for all categories + foreach (GeneralizedCategory ac in categories) + { + // Check if the action is within range of this category + if ((action >= ac.Offset) && (action < (ac.Offset + ac.Length))) return true; + } + } + + // Not generalized + return false; + } + + // This gets the generalized action category from action number + public GeneralizedCategory GetGeneralizedActionCategory(int action) + { + // Only actions above 0 + if (action > 0) + { + // Go for all categories + foreach (GeneralizedCategory ac in genactioncategories) + { + // Check if the action is within range of this category + if ((action >= ac.Offset) && (action < (ac.Offset + ac.Length))) return ac; + } + } + + // Not generalized + return null; + } + + // This checks if a specific edit mode class is listed + public bool IsEditModeSpecified(string classname) + { + return cfg.SettingExists("editingmodes." + classname.ToString(CultureInfo.InvariantCulture)); + } + + // This returns information on a linedef type + public LinedefActionInfo GetLinedefActionInfo(int action) + { + // Known type? + if (linedefactions.ContainsKey(action)) + { + return linedefactions[action]; + } + else if (action == 0) + { + return new LinedefActionInfo(0, "None", true, false); + } + else if (IsGeneralized(action, genactioncategories)) + { + return new LinedefActionInfo(action, "Generalized (" + GetGeneralizedActionCategory(action) + ")", true, true); + } + else + { + // villsa + /*if (General.Map.FormatInterface.InDoom64Mode && + (action >= 256 && action <= 511)) + { + return new LinedefActionInfo(action - 255, "Macro", false, false); + }*/ + + return new LinedefActionInfo(action, "Unknown", false, false); + } + } + + // This returns information on a sector effect + public SectorEffectInfo GetSectorEffectInfo(int effect) + { + // Known type? + if (sectoreffects.ContainsKey(effect)) + { + return sectoreffects[effect]; + } + else if (effect == 0) + { + return new SectorEffectInfo(0, "None", true, false); + } + else + { + return new SectorEffectInfo(effect, "Unknown", false, false); + } + } + + #endregion + } +} diff --git a/Source/Core/Config/GeneralizedBit.cs b/Source/Core/Config/GeneralizedBit.cs new file mode 100644 index 0000000..5635502 --- /dev/null +++ b/Source/Core/Config/GeneralizedBit.cs @@ -0,0 +1,66 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; + +namespace CodeImp.DoomBuilder.Config +{ + /// + /// Option value in generalized types. + /// + public class GeneralizedBit : INumberedTitle, IComparable + { + #region ================== Constants + + #endregion + + #region ================== Variables + + // Properties + private int index; + private string title; + + #endregion + + #region ================== Properties + + public int Index { get { return index; } } + public string Title { get { return title; } } + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + internal GeneralizedBit(int index, string title) + { + // Initialize + this.index = index; + this.title = title; + + // We have no destructor + GC.SuppressFinalize(this); + } + + #endregion + + #region ================== Methods + + // This presents the item as string + public override string ToString() + { + return title; + } + + // This compares against another + public int CompareTo(GeneralizedBit other) + { + if (this.index < other.index) return -1; + else if (this.index > other.index) return 1; + else return 0; + } + + #endregion + } +} diff --git a/Source/Core/Config/GeneralizedCategory.cs b/Source/Core/Config/GeneralizedCategory.cs new file mode 100644 index 0000000..e9f2faf --- /dev/null +++ b/Source/Core/Config/GeneralizedCategory.cs @@ -0,0 +1,99 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using CodeImp.DoomBuilder.IO; + +namespace CodeImp.DoomBuilder.Config +{ + /// + /// Category of generalized type options. + /// + public class GeneralizedCategory + { + #region ================== Constants + + #endregion + + #region ================== Variables + + // Category properties + private string title; + private int offset; + private int length; + private List options; + + // Disposing + private bool isdisposed = false; + + #endregion + + #region ================== Properties + + public string Title { get { return title; } } + public int Offset { get { return offset; } } + public int Length { get { return length; } } + public List Options { get { return options; } } + public bool IsDisposed { get { return isdisposed; } } + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + internal GeneralizedCategory(string structure, string name, Configuration cfg) + { + IDictionary opts; + + // Initialize + this.options = new List(); + + // Read properties + this.title = cfg.ReadSetting(structure + "." + name + ".title", ""); + this.offset = cfg.ReadSetting(structure + "." + name + ".offset", 0); + this.length = cfg.ReadSetting(structure + "." + name + ".length", 0); + + // Read the options + opts = cfg.ReadSetting(structure + "." + name, new Hashtable()); + foreach (DictionaryEntry de in opts) + { + // Is this an option and not just some value? + if (de.Value is IDictionary) + { + // Add the option + this.options.Add(new GeneralizedOption(structure, name, de.Key.ToString(), (IDictionary)de.Value)); + } + } + + // We have no destructor + GC.SuppressFinalize(this); + } + + // Disposer + internal void Dispose() + { + // Not already disposed? + if (!isdisposed) + { + // Clean up + options = null; + + // Done + isdisposed = true; + } + } + + #endregion + + #region ================== Methods + + // String representation + public override string ToString() + { + return title; + } + + #endregion + } +} diff --git a/Source/Core/Config/GeneralizedOption.cs b/Source/Core/Config/GeneralizedOption.cs new file mode 100644 index 0000000..a38ad4c --- /dev/null +++ b/Source/Core/Config/GeneralizedOption.cs @@ -0,0 +1,109 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Data; +using System.IO; +using System.Diagnostics; +using System.Windows.Forms; +using CodeImp.DoomBuilder.Map; + +#endregion + +namespace CodeImp.DoomBuilder.Config +{ + /// + /// Option in generalized types. + /// + public class GeneralizedOption + { + #region ================== Constants + + #endregion + + #region ================== Variables + + // Properties + private string name; + private List bits; + + #endregion + + #region ================== Properties + + public string Name { get { return name; } } + public List Bits { get { return bits; } } + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + internal GeneralizedOption(string structure, string cat, string name, IDictionary bitslist) + { + int index; + string fullpath; + + // Determine path + if (cat.Length > 0) fullpath = structure + "." + cat; + else fullpath = structure; + + // Initialize + this.name = CultureInfo.CurrentCulture.TextInfo.ToTitleCase(name); + this.bits = new List(); + + // Go for all bits + foreach (DictionaryEntry de in bitslist) + { + // Check if the item key is numeric + if (int.TryParse(de.Key.ToString(), NumberStyles.AllowLeadingWhite | NumberStyles.AllowTrailingWhite, CultureInfo.InvariantCulture, out index)) + { + // Add to list + this.bits.Add(new GeneralizedBit(index, de.Value.ToString())); + } + else + { + General.ErrorLogger.Add(ErrorType.Warning, "Structure '" + fullpath + "." + name + "' contains invalid entries. The keys must be numeric."); + } + } + + // Sort the list + bits.Sort(); + + // We have no destructor + GC.SuppressFinalize(this); + } + + #endregion + + #region ================== Methods + + // This presents the item as string + public override string ToString() + { + return name; + } + + #endregion + } +} diff --git a/Source/Core/Config/IFilledTextureSet.cs b/Source/Core/Config/IFilledTextureSet.cs new file mode 100644 index 0000000..89e09c3 --- /dev/null +++ b/Source/Core/Config/IFilledTextureSet.cs @@ -0,0 +1,42 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Data; +using System.IO; +using System.Diagnostics; +using System.Windows.Forms; +using CodeImp.DoomBuilder.Rendering; + +#endregion + +namespace CodeImp.DoomBuilder.Config +{ + internal interface IFilledTextureSet + { + // Properties + string Name { get; } + ICollection Textures { get; } + ICollection Flats { get; } + } +} diff --git a/Source/Core/Config/INumberedTitle.cs b/Source/Core/Config/INumberedTitle.cs new file mode 100644 index 0000000..f147046 --- /dev/null +++ b/Source/Core/Config/INumberedTitle.cs @@ -0,0 +1,41 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Data; +using System.IO; +using System.Diagnostics; +using System.Windows.Forms; +using CodeImp.DoomBuilder.Rendering; + +#endregion + +namespace CodeImp.DoomBuilder.Config +{ + public interface INumberedTitle + { + // Properties + int Index { get; } + string Title { get; } + } +} diff --git a/Source/Core/Config/LinedefActionCategory.cs b/Source/Core/Config/LinedefActionCategory.cs new file mode 100644 index 0000000..1c5cc3d --- /dev/null +++ b/Source/Core/Config/LinedefActionCategory.cs @@ -0,0 +1,117 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Data; +using System.IO; +using System.Diagnostics; +using System.Windows.Forms; +using CodeImp.DoomBuilder.Rendering; + +#endregion + +namespace CodeImp.DoomBuilder.Config +{ + public class LinedefActionCategory : IComparable + { + #region ================== Constants + + #endregion + + #region ================== Variables + + // Category properties + private string name; + private string title; + + // Actions + private List actions; + + // Disposing + private bool isdisposed = false; + + #endregion + + #region ================== Properties + + public string Name { get { return name; } } + public string Title { get { return title; } } + public List Actions { get { return actions; } } + public bool IsDisposed { get { return isdisposed; } } + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + internal LinedefActionCategory(string name, string title) + { + // Initialize + this.name = name; + this.title = title; + this.actions = new List(); + + // We have no destructor + GC.SuppressFinalize(this); + } + + // Disposer + internal void Dispose() + { + // Not already disposed? + if (!isdisposed) + { + // Clean up + actions = null; + + // Done + isdisposed = true; + } + } + + #endregion + + #region ================== Methods + + // This adds an action to this category + internal void Add(LinedefActionInfo a) + { + // Make it so. + actions.Add(a); + } + + // This compares against another action category + public int CompareTo(LinedefActionCategory other) + { + return string.Compare(this.name, other.name); + } + + // String representation + public override string ToString() + { + return title; + } + + #endregion + } +} diff --git a/Source/Core/Config/LinedefActionInfo.cs b/Source/Core/Config/LinedefActionInfo.cs new file mode 100644 index 0000000..03a4c10 --- /dev/null +++ b/Source/Core/Config/LinedefActionInfo.cs @@ -0,0 +1,129 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Data; +using System.IO; +using System.Diagnostics; +using System.Windows.Forms; +using CodeImp.DoomBuilder.Map; + +#endregion + +namespace CodeImp.DoomBuilder.Config +{ + public class LinedefActionInfo : INumberedTitle, IComparable + { + #region ================== Constants + + #endregion + + #region ================== Variables + + // Properties + private int index; + private string prefix; + private string category; + private string name; + private string title; + private ArgumentInfo[] args; + private bool isgeneralized; + private bool isknown; + + #endregion + + #region ================== Properties + + public int Index { get { return index; } } + public string Prefix { get { return prefix; } } + public string Category { get { return category; } } + public string Name { get { return name; } } + public string Title { get { return title; } } + public bool IsGeneralized { get { return isgeneralized; } } + public bool IsKnown { get { return isknown; } } + public bool IsNull { get { return (index == 0); } } + public ArgumentInfo[] Args { get { return args; } } + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + internal LinedefActionInfo(int index, Configuration cfg, string categoryname, IDictionary enums) + { + string actionsetting = "linedeftypes." + categoryname + "." + index.ToString(CultureInfo.InvariantCulture); + + // Initialize + this.index = index; + this.category = categoryname; + this.args = new ArgumentInfo[Linedef.NUM_ARGS]; + this.isgeneralized = false; + this.isknown = true; + + // Read settings + this.name = cfg.ReadSetting(actionsetting + ".title", "Unnamed"); + this.prefix = cfg.ReadSetting(actionsetting + ".prefix", ""); + this.title = this.prefix + " " + this.name; + this.title = this.title.Trim(); + + // Read the args + for (int i = 0; i < Linedef.NUM_ARGS; i++) + this.args[i] = new ArgumentInfo(cfg, actionsetting, i, enums); + + // We have no destructor + GC.SuppressFinalize(this); + } + + // Constructor for generalized type display + internal LinedefActionInfo(int index, string title, bool isknown, bool isgeneralized) + { + this.index = index; + this.isgeneralized = isgeneralized; + this.isknown = isknown; + this.title = title; + this.args = new ArgumentInfo[Linedef.NUM_ARGS]; + for (int i = 0; i < Linedef.NUM_ARGS; i++) + this.args[i] = new ArgumentInfo(i); + } + + #endregion + + #region ================== Methods + + // This presents the item as string + public override string ToString() + { + return index + " - " + title; + } + + // This compares against another action info + public int CompareTo(LinedefActionInfo other) + { + if (this.index < other.index) return -1; + else if (this.index > other.index) return 1; + else return 0; + } + + #endregion + } +} diff --git a/Source/Core/Config/LinedefActivateInfo.cs b/Source/Core/Config/LinedefActivateInfo.cs new file mode 100644 index 0000000..e13ecde --- /dev/null +++ b/Source/Core/Config/LinedefActivateInfo.cs @@ -0,0 +1,94 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Data; +using System.IO; +using System.Diagnostics; +using System.Windows.Forms; +using CodeImp.DoomBuilder.Map; + +#endregion + +namespace CodeImp.DoomBuilder.Config +{ + public class LinedefActivateInfo : IComparable + { + #region ================== Constants + + #endregion + + #region ================== Variables + + // Properties + private int intkey; + private string key; + private string title; + + #endregion + + #region ================== Properties + + public int Index { get { return intkey; } } + public string Key { get { return key; } } + public string Title { get { return title; } } + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + internal LinedefActivateInfo(string key, string title) + { + // Initialize + this.key = key; + this.title = title; + + // Try parsing key as int for comparison + if (!int.TryParse(key, out intkey)) intkey = 0; + + // We have no destructor + GC.SuppressFinalize(this); + } + + #endregion + + #region ================== Methods + + // This presents the item as string + public override string ToString() + { + return title; + } + + // This compares against another activate info + public int CompareTo(LinedefActivateInfo other) + { + if (this.intkey < other.intkey) return -1; + else if (this.intkey > other.intkey) return 1; + else return 0; + } + + #endregion + } +} diff --git a/Source/Core/Config/MapLumpInfo.cs b/Source/Core/Config/MapLumpInfo.cs new file mode 100644 index 0000000..fe1223e --- /dev/null +++ b/Source/Core/Config/MapLumpInfo.cs @@ -0,0 +1,76 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Data; +using System.IO; +using System.Diagnostics; +using System.Windows.Forms; +using CodeImp.DoomBuilder.Map; +using System.Collections.ObjectModel; + +#endregion + +namespace CodeImp.DoomBuilder.Config +{ + public struct MapLumpInfo + { + // Members + public string name; + public bool required; + public bool blindcopy; + public bool nodebuild; + public bool allowempty; + internal ScriptConfiguration script; + + // Construct from IDictionary + internal MapLumpInfo(string name, Configuration cfg) + { + string scriptconfig = ""; + + // Apply settings + this.name = name; + this.script = null; + this.required = cfg.ReadSetting("maplumpnames." + name + ".required", false); + this.blindcopy = cfg.ReadSetting("maplumpnames." + name + ".blindcopy", false); + this.nodebuild = cfg.ReadSetting("maplumpnames." + name + ".nodebuild", false); + this.allowempty = cfg.ReadSetting("maplumpnames." + name + ".allowempty", false); + scriptconfig = cfg.ReadSetting("maplumpnames." + name + ".script", ""); + + // Find script configuration + if (scriptconfig.Length > 0) + { + if (General.ScriptConfigs.ContainsKey(scriptconfig.ToLowerInvariant())) + { + this.script = General.ScriptConfigs[scriptconfig.ToLowerInvariant()]; + } + else + { + General.ErrorLogger.Add(ErrorType.Warning, "Map lump '" + name + "' in the current game configuration specifies an unknown script configuration '" + scriptconfig + "'. Using plain text instead."); + this.script = new ScriptConfiguration(); + } + } + } + } +} + diff --git a/Source/Core/Config/MatchingTextureSet.cs b/Source/Core/Config/MatchingTextureSet.cs new file mode 100644 index 0000000..6857de3 --- /dev/null +++ b/Source/Core/Config/MatchingTextureSet.cs @@ -0,0 +1,195 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Data; +using System.IO; +using System.Diagnostics; +using System.Windows.Forms; +using System.Text.RegularExpressions; +using System.Collections.Specialized; + +#endregion + +namespace CodeImp.DoomBuilder.Config +{ + internal sealed class MatchingTextureSet : TextureSet, IFilledTextureSet, IComparable + { + #region ================== Variables + + // Never stored, only used at run-time + private Regex regex; + + // Matching textures and flats + private List textures; + private List flats; + + #endregion + + #region ================== Properties + + public ICollection Textures { get { return textures; } } + public ICollection Flats { get { return flats; } } + + #endregion + + #region ================== Constructor / Destructor + + // New texture set for quick matching + public MatchingTextureSet(ICollection filters) + { + this.filters = new List(filters); + + // Setup + Setup(); + } + + // Texture set from defined set + public MatchingTextureSet(DefinedTextureSet definedset) + { + // Copy the name + this.name = definedset.Name; + + // Copy the filters + this.filters = new List(definedset.Filters); + + // Setup + Setup(); + } + + #endregion + + #region ================== Methods + + // This sets up the object + private void Setup() + { + // Make the regex string that handles all filters + StringBuilder regexstr = new StringBuilder(""); + foreach (string s in this.filters) + { + // Make sure filter is in uppercase + string ss = s.ToUpperInvariant(); + + // Escape regex characters + ss = ss.Replace("+", "\\+"); + ss = ss.Replace("\\", "\\\\"); + ss = ss.Replace("|", "\\|"); + ss = ss.Replace("{", "\\{"); + ss = ss.Replace("[", "\\["); + ss = ss.Replace("(", "\\("); + ss = ss.Replace(")", "\\)"); + ss = ss.Replace("^", "\\^"); + ss = ss.Replace("$", "\\$"); + ss = ss.Replace(".", "\\."); + ss = ss.Replace("#", "\\#"); + ss = ss.Replace(" ", "\\ "); + + // Replace the ? with the regex code for single character + ss = ss.Replace("?", "."); + + // Replace the * with the regex code for optional multiple characters + ss = ss.Replace("*", ".*?"); + + // When a filter has already added, insert a conditional OR operator + if (regexstr.Length > 0) regexstr.Append("|"); + + // Open group without backreferencing + regexstr.Append("(?:"); + + // Must be start of string + regexstr.Append("\\A"); + + // Add the filter + regexstr.Append(ss); + + // Must be end of string + regexstr.Append("\\Z"); + + // Close group + regexstr.Append(")"); + } + + // No filters added? Then make a never-matching regex + if (this.filters.Count == 0) regexstr.Append("\\Z\\A"); + + // Make the regex + regex = new Regex(regexstr.ToString(), RegexOptions.Compiled | + RegexOptions.CultureInvariant); + + // Initialize collections + textures = new List(); + flats = new List(); + } + + // This matches a name against the regex and adds a texture to + // the list if it matches. Returns true when matched and added. + internal bool AddTexture(ImageData image) + { + // Check against regex + if (regex.IsMatch(image.Name.ToUpperInvariant())) + { + // Matches! Add it. + textures.Add(image); + return true; + } + else + { + // Doesn't match + return false; + } + } + + // This matches a name against the regex and adds a flat to + // the list if it matches. Returns true when matched and added. + internal bool AddFlat(ImageData image) + { + // Check against regex + if (regex.IsMatch(image.Name.ToUpperInvariant())) + { + // Matches! Add it. + flats.Add(image); + return true; + } + else + { + // Doesn't match + return false; + } + } + + // This only checks if the given image is a match + internal bool IsMatch(ImageData image) + { + return regex.IsMatch(image.Name.ToUpperInvariant()); + } + + // This compares it for sorting + public int CompareTo(MatchingTextureSet other) + { + return string.Compare(this.name, other.name); + } + + #endregion + } +} diff --git a/Source/Core/Config/NodebuilderInfo.cs b/Source/Core/Config/NodebuilderInfo.cs new file mode 100644 index 0000000..978a5cd --- /dev/null +++ b/Source/Core/Config/NodebuilderInfo.cs @@ -0,0 +1,132 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Data; +using System.IO; +using System.Diagnostics; +using System.Windows.Forms; +using CodeImp.DoomBuilder.Compilers; + +#endregion + +namespace CodeImp.DoomBuilder.Config +{ + internal class NodebuilderInfo : IComparable + { + #region ================== Constants + + #endregion + + #region ================== Variables + + private string name; + private string title; + private CompilerInfo compiler; + private string parameters; + private bool specialoutputfile; + + #endregion + + #region ================== Properties + + public string Name { get { return name; } } + public string Title { get { return title; } } + public CompilerInfo Compiler { get { return compiler; } } + public string Parameters { get { return parameters; } } + public bool HasSpecialOutputFile { get { return specialoutputfile; } } + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + public NodebuilderInfo(string filename, string name, Configuration cfg) + { + string compilername; + + General.WriteLogLine("Registered nodebuilder configuration '" + name + "' from '" + filename + "'"); + + // Initialize + this.name = name; + this.compiler = null; + this.title = cfg.ReadSetting("nodebuilders." + name + ".title", ""); + compilername = cfg.ReadSetting("nodebuilders." + name + ".compiler", ""); + this.parameters = cfg.ReadSetting("nodebuilders." + name + ".parameters", ""); + + // Check for special output filename + this.specialoutputfile = this.parameters.Contains("%FO"); + + // Find compiler + foreach (CompilerInfo c in General.Compilers) + { + // Compiler name matches? + if (c.Name == compilername) + { + // Apply compiler + this.compiler = c; + break; + } + } + + // No compiler found? + if (this.compiler == null) throw new Exception("No such compiler defined: '" + compilername + "'"); + } + + // Constructor for "none" nodebuilder + public NodebuilderInfo() + { + // Initialize + this.name = ""; + this.compiler = null; + this.title = "[do not build nodes]"; + this.parameters = ""; + this.specialoutputfile = false; + } + + #endregion + + #region ================== Methods + + // This compares it to other ConfigurationInfo objects + public int CompareTo(NodebuilderInfo other) + { + // Compare + return name.CompareTo(other.name); + } + + // String representation + public override string ToString() + { + return title; + } + + // This runs the nodebuilder + public Compiler CreateCompiler() + { + return compiler.Create(); + } + + #endregion + } +} diff --git a/Source/Core/Config/PasteOptions.cs b/Source/Core/Config/PasteOptions.cs new file mode 100644 index 0000000..1c14a4a --- /dev/null +++ b/Source/Core/Config/PasteOptions.cs @@ -0,0 +1,104 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Data; +using System.IO; +using System.Diagnostics; +using System.Windows.Forms; +using CodeImp.DoomBuilder.Compilers; + +#endregion + +namespace CodeImp.DoomBuilder.Config +{ + public class PasteOptions + { + #region ================== Constants + + public const int TAGS_KEEP = 0; + public const int TAGS_RENUMBER = 1; + public const int TAGS_REMOVE = 2; + + #endregion + + #region ================== Variables + + private int changetags; // See TAGS_ constants + private bool removeactions; + private bool adjustheights; + + #endregion + + #region ================== Properties + + public int ChangeTags { get { return changetags; } set { changetags = value; } } + public bool RemoveActions { get { return removeactions; } set { removeactions = value; } } + public bool AdjustHeights { get { return adjustheights; } set { adjustheights = value; } } + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + public PasteOptions() + { + } + + // Copy Constructor + public PasteOptions(PasteOptions p) + { + this.changetags = p.changetags; + this.removeactions = p.removeactions; + this.adjustheights = p.adjustheights; + } + + #endregion + + #region ================== Methods + + // Make a copy + public PasteOptions Copy() + { + return new PasteOptions(this); + } + + // This reads from configuration + internal void ReadConfiguration(Configuration cfg, string path) + { + changetags = cfg.ReadSetting(path + ".changetags", 0); + removeactions = cfg.ReadSetting(path + ".removeactions", false); + adjustheights = cfg.ReadSetting(path + ".adjustheights", true); + } + + // This writes to configuration + internal void WriteConfiguration(Configuration cfg, string path) + { + cfg.WriteSetting(path + ".changetags", changetags); + cfg.WriteSetting(path + ".removeactions", removeactions); + cfg.WriteSetting(path + ".adjustheights", adjustheights); + } + + #endregion + } +} diff --git a/Source/Core/Config/ProgramConfiguration.cs b/Source/Core/Config/ProgramConfiguration.cs new file mode 100644 index 0000000..525bafd --- /dev/null +++ b/Source/Core/Config/ProgramConfiguration.cs @@ -0,0 +1,584 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Data; +using System.IO; +using System.Diagnostics; +using System.Windows.Forms; +using CodeImp.DoomBuilder.Map; +using System.Reflection; +using CodeImp.DoomBuilder.Plugins; +using CodeImp.DoomBuilder.Editing; +using CodeImp.DoomBuilder.Rendering; + +#endregion + +namespace CodeImp.DoomBuilder.Config +{ + public class ProgramConfiguration + { + #region ================== Constants + + #endregion + + #region ================== Variables + + // Original configuration + private Configuration cfg; + + // Cached variables + //private int undolevels; + private bool blackbrowsers; + private int visualfov; + private int lightintensity; // villsa + private float visualmousesensx; + private float visualmousesensy; + private int imagebrightness; + private float doublesidedalpha; + private byte doublesidedalphabyte; + private float backgroundalpha; + private bool qualitydisplay; + private bool squarethings; + private bool testmonsters; + private int defaultviewmode; + private bool classicbilinear; + private bool visualbilinear; + private int mousespeed; + private int movespeed; + private float viewdistance; + private bool invertyaxis; + private string scriptfontname; + private int scriptfontsize; + private bool scriptfontbold; + private bool scriptontop; + private bool scriptautoindent; + private int scripttabwidth; + private int previewimagesize; + private int autoscrollspeed; + private int zoomfactor; + private bool showerrorswindow; + private bool animatevisualselection; + private int previousversion; + private PasteOptions pasteoptions; + private int dockersposition; + private bool collapsedockers; + private int dockerswidth; + private bool toolbarscript; + private bool toolbarundo; + private bool toolbarcopy; + private bool toolbarprefabs; + private bool toolbarfilter; + private bool toolbarviewmodes; + private bool toolbargeometry; + private bool toolbartesting; + private bool toolbarfile; + + // These are not stored in the configuration, only used at runtime + private string defaulttexture; + private int defaultbrightness = 192; + private int defaultfloorheight = 0; + private int defaultceilheight = 128; + private string defaultfloortexture; + private string defaultceiltexture; + private int defaultthingtype = 1; + private float defaultthingangle = 0.0f; + private List defaultthingflags; + + #endregion + + #region ================== Properties + + internal Configuration Config { get { return cfg; } } + //public int UndoLevels { get { return undolevels; } internal set { undolevels = value; } } + public bool BlackBrowsers { get { return blackbrowsers; } internal set { blackbrowsers = value; } } + public int VisualFOV { get { return visualfov; } internal set { visualfov = value; } } + public int LightIntensity { get { return lightintensity; } internal set { lightintensity = value; } } // villsa + public int ImageBrightness { get { return imagebrightness; } internal set { imagebrightness = value; } } + public float DoubleSidedAlpha { get { return doublesidedalpha; } internal set { doublesidedalpha = value; doublesidedalphabyte = (byte)(doublesidedalpha * 255f); } } + public byte DoubleSidedAlphaByte { get { return doublesidedalphabyte; } } + public float BackgroundAlpha { get { return backgroundalpha; } internal set { backgroundalpha = value; } } + public float VisualMouseSensX { get { return visualmousesensx; } internal set { visualmousesensx = value; } } + public float VisualMouseSensY { get { return visualmousesensy; } internal set { visualmousesensy = value; } } + public bool QualityDisplay { get { return qualitydisplay; } internal set { qualitydisplay = value; } } + public bool SquareThings { get { return squarethings; } internal set { squarethings = value; } } + public bool TestMonsters { get { return testmonsters; } internal set { testmonsters = value; } } + public int DefaultViewMode { get { return defaultviewmode; } internal set { defaultviewmode = value; } } + public bool ClassicBilinear { get { return classicbilinear; } internal set { classicbilinear = value; } } + public bool VisualBilinear { get { return visualbilinear; } internal set { visualbilinear = value; } } + public int MouseSpeed { get { return mousespeed; } internal set { mousespeed = value; } } + public int MoveSpeed { get { return movespeed; } internal set { movespeed = value; } } + public float ViewDistance { get { return viewdistance; } internal set { viewdistance = value; } } + public bool InvertYAxis { get { return invertyaxis; } internal set { invertyaxis = value; } } + public string ScriptFontName { get { return scriptfontname; } internal set { scriptfontname = value; } } + public int ScriptFontSize { get { return scriptfontsize; } internal set { scriptfontsize = value; } } + public bool ScriptFontBold { get { return scriptfontbold; } internal set { scriptfontbold = value; } } + public bool ScriptOnTop { get { return scriptontop; } internal set { scriptontop = value; } } + public int PreviewImageSize { get { return previewimagesize; } internal set { previewimagesize = value; } } + public int AutoScrollSpeed { get { return autoscrollspeed; } internal set { autoscrollspeed = value; } } + public int ZoomFactor { get { return zoomfactor; } internal set { zoomfactor = value; } } + public bool ShowErrorsWindow { get { return showerrorswindow; } internal set { showerrorswindow = value; } } + public bool AnimateVisualSelection { get { return animatevisualselection; } internal set { animatevisualselection = value; } } + public int ScriptTabWidth { get { return scripttabwidth; } internal set { scripttabwidth = value; } } + public bool ScriptAutoIndent { get { return scriptautoindent; } internal set { scriptautoindent = value; } } + internal int PreviousVersion { get { return previousversion; } } + internal PasteOptions PasteOptions { get { return pasteoptions; } set { pasteoptions = value; } } + public int DockersPosition { get { return dockersposition; } internal set { dockersposition = value; } } + public bool CollapseDockers { get { return collapsedockers; } internal set { collapsedockers = value; } } + public int DockersWidth { get { return dockerswidth; } internal set { dockerswidth = value; } } + public bool ToolbarScript { get { return toolbarscript; } internal set { toolbarscript = value; } } + public bool ToolbarUndo { get { return toolbarundo; } internal set { toolbarundo = value; } } + public bool ToolbarCopy { get { return toolbarcopy; } internal set { toolbarcopy = value; } } + public bool ToolbarPrefabs { get { return toolbarprefabs; } internal set { toolbarprefabs = value; } } + public bool ToolbarFilter { get { return toolbarfilter; } internal set { toolbarfilter = value; } } + public bool ToolbarViewModes { get { return toolbarviewmodes; } internal set { toolbarviewmodes = value; } } + public bool ToolbarGeometry { get { return toolbargeometry; } internal set { toolbargeometry = value; } } + public bool ToolbarTesting { get { return toolbartesting; } internal set { toolbartesting = value; } } + public bool ToolbarFile { get { return toolbarfile; } internal set { toolbarfile = value; } } + + public string DefaultTexture { get { return defaulttexture; } set { defaulttexture = value; } } + public string DefaultFloorTexture { get { return defaultfloortexture; } set { defaultfloortexture = value; } } + public string DefaultCeilingTexture { get { return defaultceiltexture; } set { defaultceiltexture = value; } } + public int DefaultBrightness { get { return defaultbrightness; } set { defaultbrightness = value; } } + public int DefaultFloorHeight { get { return defaultfloorheight; } set { defaultfloorheight = value; } } + public int DefaultCeilingHeight { get { return defaultceilheight; } set { defaultceilheight = value; } } + public int DefaultThingType { get { return defaultthingtype; } set { defaultthingtype = value; } } + public float DefaultThingAngle { get { return defaultthingangle; } set { defaultthingangle = value; } } + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + internal ProgramConfiguration() + { + // We have no destructor + GC.SuppressFinalize(this); + defaultthingflags = new List(); + pasteoptions = new PasteOptions(); + } + + #endregion + + #region ================== Loading / Saving + + // This loads the program configuration + internal bool Load(string cfgfilepathname, string defaultfilepathname) + { + // First parse it + if (Read(cfgfilepathname, defaultfilepathname)) + { + // Read the cache variables + blackbrowsers = cfg.ReadSetting("blackbrowsers", false); + //undolevels = cfg.ReadSetting("undolevels", 20); + visualfov = cfg.ReadSetting("visualfov", 100); + lightintensity = cfg.ReadSetting("lightintensity", 0); // villsa + visualmousesensx = cfg.ReadSetting("visualmousesensx", 40f); + visualmousesensy = cfg.ReadSetting("visualmousesensy", 40f); + imagebrightness = cfg.ReadSetting("imagebrightness", 3); + doublesidedalpha = cfg.ReadSetting("doublesidedalpha", 0.4f); + doublesidedalphabyte = (byte)(doublesidedalpha * 255f); + backgroundalpha = cfg.ReadSetting("backgroundalpha", 1.0f); + qualitydisplay = cfg.ReadSetting("qualitydisplay", true); + squarethings = cfg.ReadSetting("squarethings", true); + testmonsters = cfg.ReadSetting("testmonsters", true); + defaultviewmode = cfg.ReadSetting("defaultviewmode", (int)ViewMode.Normal); + classicbilinear = cfg.ReadSetting("classicbilinear", true); + visualbilinear = cfg.ReadSetting("visualbilinear", true); + mousespeed = cfg.ReadSetting("mousespeed", 100); + movespeed = cfg.ReadSetting("movespeed", 500); + viewdistance = cfg.ReadSetting("viewdistance", 3000.0f); + invertyaxis = cfg.ReadSetting("invertyaxis", false); + scriptfontname = cfg.ReadSetting("scriptfontname", "Lucida Console"); + scriptfontsize = cfg.ReadSetting("scriptfontsize", 10); + scriptfontbold = cfg.ReadSetting("scriptfontbold", false); + scriptautoindent = cfg.ReadSetting("scriptautoindent", true); + scriptontop = cfg.ReadSetting("scriptontop", true); + scripttabwidth = cfg.ReadSetting("scripttabwidth", 4); + previewimagesize = cfg.ReadSetting("previewimagesize", 1); + autoscrollspeed = cfg.ReadSetting("autoscrollspeed", 0); + zoomfactor = cfg.ReadSetting("zoomfactor", 3); + showerrorswindow = cfg.ReadSetting("showerrorswindow", true); + animatevisualselection = cfg.ReadSetting("animatevisualselection", true); + previousversion = cfg.ReadSetting("currentversion", 0); + dockersposition = cfg.ReadSetting("dockersposition", 1); + collapsedockers = cfg.ReadSetting("collapsedockers", true); + dockerswidth = cfg.ReadSetting("dockerswidth", 300); + pasteoptions.ReadConfiguration(cfg, "pasteoptions"); + toolbarscript = cfg.ReadSetting("toolbarscript", true); + toolbarundo = cfg.ReadSetting("toolbarundo", false); + toolbarcopy = cfg.ReadSetting("toolbarcopy", false); + toolbarprefabs = cfg.ReadSetting("toolbarprefabs", true); + toolbarfilter = cfg.ReadSetting("toolbarfilter", true); + toolbarviewmodes = cfg.ReadSetting("toolbarviewmodes", false); + toolbargeometry = cfg.ReadSetting("toolbargeometry", true); + toolbartesting = cfg.ReadSetting("toolbartesting", true); + toolbarfile = cfg.ReadSetting("toolbarfile", true); + + // Success + return true; + } + else + { + // Failed + return false; + } + } + + // This saves the program configuration + internal void Save(string filepathname) + { + Version v = General.ThisAssembly.GetName().Version; + + // Write the cache variables + cfg.WriteSetting("blackbrowsers", blackbrowsers); + //cfg.WriteSetting("undolevels", undolevels); + cfg.WriteSetting("visualfov", visualfov); + cfg.WriteSetting("lightintensity", lightintensity); // villsa + cfg.WriteSetting("visualmousesensx", visualmousesensx); + cfg.WriteSetting("visualmousesensy", visualmousesensy); + cfg.WriteSetting("imagebrightness", imagebrightness); + cfg.WriteSetting("qualitydisplay", qualitydisplay); + cfg.WriteSetting("squarethings", squarethings); + cfg.WriteSetting("testmonsters", testmonsters); + cfg.WriteSetting("doublesidedalpha", doublesidedalpha); + cfg.WriteSetting("backgroundalpha", backgroundalpha); + cfg.WriteSetting("defaultviewmode", defaultviewmode); + cfg.WriteSetting("classicbilinear", classicbilinear); + cfg.WriteSetting("visualbilinear", visualbilinear); + cfg.WriteSetting("mousespeed", mousespeed); + cfg.WriteSetting("movespeed", movespeed); + cfg.WriteSetting("viewdistance", viewdistance); + cfg.WriteSetting("invertyaxis", invertyaxis); + cfg.WriteSetting("scriptfontname", scriptfontname); + cfg.WriteSetting("scriptfontsize", scriptfontsize); + cfg.WriteSetting("scriptfontbold", scriptfontbold); + cfg.WriteSetting("scriptontop", scriptontop); + cfg.WriteSetting("scripttabwidth", scripttabwidth); + cfg.WriteSetting("scriptautoindent", scriptautoindent); + cfg.WriteSetting("previewimagesize", previewimagesize); + cfg.WriteSetting("autoscrollspeed", autoscrollspeed); + cfg.WriteSetting("zoomfactor", zoomfactor); + cfg.WriteSetting("showerrorswindow", showerrorswindow); + cfg.WriteSetting("animatevisualselection", animatevisualselection); + cfg.WriteSetting("currentversion", v.Major * 1000000 + v.Revision); + cfg.WriteSetting("dockersposition", dockersposition); + cfg.WriteSetting("collapsedockers", collapsedockers); + cfg.WriteSetting("dockerswidth", dockerswidth); + pasteoptions.WriteConfiguration(cfg, "pasteoptions"); + cfg.WriteSetting("toolbarscript", toolbarscript); + cfg.WriteSetting("toolbarundo", toolbarundo); + cfg.WriteSetting("toolbarcopy", toolbarcopy); + cfg.WriteSetting("toolbarprefabs", toolbarprefabs); + cfg.WriteSetting("toolbarfilter", toolbarfilter); + cfg.WriteSetting("toolbarviewmodes", toolbarviewmodes); + cfg.WriteSetting("toolbargeometry", toolbargeometry); + cfg.WriteSetting("toolbartesting", toolbartesting); + cfg.WriteSetting("toolbarfile", toolbarfile); + + // Save settings configuration + General.WriteLogLine("Saving program configuration..."); + cfg.SaveConfiguration(filepathname); + } + + // This reads the configuration + private bool Read(string cfgfilepathname, string defaultfilepathname) + { + DialogResult result; + + // Check if no config for this user exists yet + if (!File.Exists(cfgfilepathname)) + { + // Copy new configuration + General.WriteLogLine("Local user program configuration is missing!"); + File.Copy(defaultfilepathname, cfgfilepathname); + General.WriteLogLine("New program configuration copied for local user"); + } + + // Load it + cfg = new Configuration(cfgfilepathname, true); + if (cfg.ErrorResult) + { + // Error in configuration + // Ask user for a new copy + result = General.ShowErrorMessage("Error in program configuration near line " + cfg.ErrorLine + ": " + cfg.ErrorDescription + "\n\nWould you like to overwrite your settings with a new configuration to restore the default settings?", MessageBoxButtons.YesNoCancel); + if (result == DialogResult.Yes) + { + // Remove old configuration and make a new copy + General.WriteLogLine("User requested a new copy of the program configuration"); + File.Delete(cfgfilepathname); + File.Copy(defaultfilepathname, cfgfilepathname); + General.WriteLogLine("New program configuration copied for local user"); + + // Load it + cfg = new Configuration(cfgfilepathname, true); + if (cfg.ErrorResult) + { + // Error in configuration + General.WriteLogLine("Error in program configuration near line " + cfg.ErrorLine + ": " + cfg.ErrorDescription); + General.ShowErrorMessage("Default program configuration is corrupted. Please re-install Doom Builder.", MessageBoxButtons.OK); + return false; + } + } + else if (result == DialogResult.Cancel) + { + // User requested to cancel startup + General.WriteLogLine("User cancelled startup"); + return false; + } + } + + // Check if a version number is missing + previousversion = cfg.ReadSetting("currentversion", -1); + if (previousversion == -1) + { + // Remove old configuration and make a new copy + General.WriteLogLine("Program configuration is outdated, new configuration will be copied for local user"); + File.Delete(cfgfilepathname); + File.Copy(defaultfilepathname, cfgfilepathname); + + // Load it + cfg = new Configuration(cfgfilepathname, true); + if (cfg.ErrorResult) + { + // Error in configuration + General.WriteLogLine("Error in program configuration near line " + cfg.ErrorLine + ": " + cfg.ErrorDescription); + General.ShowErrorMessage("Default program configuration is corrupted. Please re-install Doom Builder.", MessageBoxButtons.OK); + return false; + } + } + + // Success + return true; + } + + #endregion + + #region ================== Methods + + // This makes the path prefix for the given assembly + private string GetPluginPathPrefix(Assembly asm) + { + Plugin p = General.Plugins.FindPluginByAssembly(asm); + return "plugins." + p.Name.ToLowerInvariant() + "."; + } + + // ReadPluginSetting + public string ReadPluginSetting(string setting, string defaultsetting) { return cfg.ReadSetting(GetPluginPathPrefix(Assembly.GetCallingAssembly()) + setting, defaultsetting); } + public int ReadPluginSetting(string setting, int defaultsetting) { return cfg.ReadSetting(GetPluginPathPrefix(Assembly.GetCallingAssembly()) + setting, defaultsetting); } + public float ReadPluginSetting(string setting, float defaultsetting) { return cfg.ReadSetting(GetPluginPathPrefix(Assembly.GetCallingAssembly()) + setting, defaultsetting); } + public short ReadPluginSetting(string setting, short defaultsetting) { return cfg.ReadSetting(GetPluginPathPrefix(Assembly.GetCallingAssembly()) + setting, defaultsetting); } + public long ReadPluginSetting(string setting, long defaultsetting) { return cfg.ReadSetting(GetPluginPathPrefix(Assembly.GetCallingAssembly()) + setting, defaultsetting); } + public bool ReadPluginSetting(string setting, bool defaultsetting) { return cfg.ReadSetting(GetPluginPathPrefix(Assembly.GetCallingAssembly()) + setting, defaultsetting); } + public byte ReadPluginSetting(string setting, byte defaultsetting) { return cfg.ReadSetting(GetPluginPathPrefix(Assembly.GetCallingAssembly()) + setting, defaultsetting); } + public IDictionary ReadPluginSetting(string setting, IDictionary defaultsetting) { return cfg.ReadSetting(GetPluginPathPrefix(Assembly.GetCallingAssembly()) + setting, defaultsetting); } + + // ReadPluginSetting with specific plugin + public string ReadPluginSetting(string pluginname, string setting, string defaultsetting) { return cfg.ReadSetting(pluginname.ToLowerInvariant() + "." + setting, defaultsetting); } + public int ReadPluginSetting(string pluginname, string setting, int defaultsetting) { return cfg.ReadSetting(pluginname.ToLowerInvariant() + "." + setting, defaultsetting); } + public float ReadPluginSetting(string pluginname, string setting, float defaultsetting) { return cfg.ReadSetting(pluginname.ToLowerInvariant() + "." + setting, defaultsetting); } + public short ReadPluginSetting(string pluginname, string setting, short defaultsetting) { return cfg.ReadSetting(pluginname.ToLowerInvariant() + "." + setting, defaultsetting); } + public long ReadPluginSetting(string pluginname, string setting, long defaultsetting) { return cfg.ReadSetting(pluginname.ToLowerInvariant() + "." + setting, defaultsetting); } + public bool ReadPluginSetting(string pluginname, string setting, bool defaultsetting) { return cfg.ReadSetting(pluginname.ToLowerInvariant() + "." + setting, defaultsetting); } + public byte ReadPluginSetting(string pluginname, string setting, byte defaultsetting) { return cfg.ReadSetting(pluginname.ToLowerInvariant() + "." + setting, defaultsetting); } + public IDictionary ReadPluginSetting(string pluginname, string setting, IDictionary defaultsetting) { return cfg.ReadSetting(pluginname.ToLowerInvariant() + "." + setting, defaultsetting); } + + // WritePluginSetting + public bool WritePluginSetting(string setting, object settingvalue) { return cfg.WriteSetting(GetPluginPathPrefix(Assembly.GetCallingAssembly()) + setting, settingvalue); } + + // DeletePluginSetting + public bool DeletePluginSetting(string setting) { return cfg.DeleteSetting(GetPluginPathPrefix(Assembly.GetCallingAssembly()) + setting); } + + // ReadSetting + internal string ReadSetting(string setting, string defaultsetting) { return cfg.ReadSetting(setting, defaultsetting); } + internal int ReadSetting(string setting, int defaultsetting) { return cfg.ReadSetting(setting, defaultsetting); } + internal float ReadSetting(string setting, float defaultsetting) { return cfg.ReadSetting(setting, defaultsetting); } + internal short ReadSetting(string setting, short defaultsetting) { return cfg.ReadSetting(setting, defaultsetting); } + internal long ReadSetting(string setting, long defaultsetting) { return cfg.ReadSetting(setting, defaultsetting); } + internal bool ReadSetting(string setting, bool defaultsetting) { return cfg.ReadSetting(setting, defaultsetting); } + internal byte ReadSetting(string setting, byte defaultsetting) { return cfg.ReadSetting(setting, defaultsetting); } + internal IDictionary ReadSetting(string setting, IDictionary defaultsetting) { return cfg.ReadSetting(setting, defaultsetting); } + + // WriteSetting + internal bool WriteSetting(string setting, object settingvalue) { return cfg.WriteSetting(setting, settingvalue); } + internal bool WriteSetting(string setting, object settingvalue, string pathseperator) { return cfg.WriteSetting(setting, settingvalue, pathseperator); } + + #endregion + + #region ================== Default Settings + + // This sets the default thing flags + public void SetDefaultThingFlags(ICollection setflags) + { + defaultthingflags = new List(setflags); + } + + // This applies default settings to a thing + public void ApplyDefaultThingSettings(Thing t) + { + t.Type = defaultthingtype; + t.Rotate(defaultthingangle); + foreach (string f in defaultthingflags) t.SetFlag(f, true); + } + + // This attempts to find the default drawing settings + public void FindDefaultDrawSettings() + { + bool foundone; + + // Only possible when a map is loaded + if (General.Map == null) return; + + // Default texture missing? + if ((defaulttexture == null) || defaulttexture.StartsWith("-")) + { + // Find default texture from map + foundone = false; + foreach (Sidedef sd in General.Map.Map.Sidedefs) + { + if (!sd.MiddleTexture.StartsWith("-")) + { + defaulttexture = sd.MiddleTexture; + if (General.Map.Data.GetTextureExists(defaulttexture)) + { + foundone = true; + break; + } + } + } + + // Not found yet? + if (!foundone) + { + // Pick the first STARTAN from the list. + // I love the STARTAN texture as default for some reason. + foreach (string s in General.Map.Data.TextureNames) + { + if (s.StartsWith("SPACEAP")) // villsa 9/11/11 (builder64) + { + foundone = true; + defaulttexture = s; + break; + } + } + + // Otherwise just pick the first + if (!foundone) + { + if (General.Map.Data.TextureNames.Count > 1) + defaulttexture = General.Map.Data.TextureNames[1]; + } + } + } + + // Default floor missing? + if ((defaultfloortexture == null) || (defaultfloortexture.Length == 0)) + { + // Find default texture from map + foundone = false; + if (General.Map.Map.Sectors.Count > 0) + { + // Find one that is known + foreach (Sector s in General.Map.Map.Sectors) + { + defaultfloortexture = s.FloorTexture; + if (General.Map.Data.GetFlatExists(defaultfloortexture)) + { + foundone = true; + break; + } + } + } + + // Pick the first FLOOR from the list. + if (!foundone) + { + foreach (string s in General.Map.Data.FlatNames) + { + if (s.StartsWith("SFLATA")) // villsa 9/11/11 (builder64) + { + foundone = true; + defaultfloortexture = s; + break; + } + } + } + + // Otherwise just pick the first + if (!foundone) + { + if (General.Map.Data.FlatNames.Count > 0) + defaultfloortexture = General.Map.Data.FlatNames[0]; + } + } + + // Default ceiling missing? + if ((defaultceiltexture == null) || (defaultceiltexture.Length == 0)) + { + // Find default texture from map + foundone = false; + if (General.Map.Map.Sectors.Count > 0) + { + // Find one that is known + foreach (Sector s in General.Map.Map.Sectors) + { + defaultceiltexture = s.CeilTexture; + if (General.Map.Data.GetFlatExists(defaultceiltexture)) + { + foundone = true; + break; + } + } + } + + // Pick the first CEIL from the list. + if (!foundone) + { + foreach (string s in General.Map.Data.FlatNames) + { + if (s.StartsWith("SFLATA")) // villsa 9/11/11 (builder64) + { + foundone = true; + defaultceiltexture = s; + break; + } + } + } + + // Otherwise just pick the first + if (!foundone) + { + if (General.Map.Data.FlatNames.Count > 1) + defaultceiltexture = General.Map.Data.FlatNames[1]; + } + } + + // Texture names may not be null + if ((defaulttexture == null) || (defaulttexture == "")) defaulttexture = "-"; + if ((defaultfloortexture == null) || (defaultfloortexture == "")) defaultfloortexture = "-"; + if ((defaultceiltexture == null) || (defaultceiltexture == "")) defaultceiltexture = "-"; + } + + #endregion + } +} diff --git a/Source/Core/Config/ResourceTextureSet.cs b/Source/Core/Config/ResourceTextureSet.cs new file mode 100644 index 0000000..8d1dc42 --- /dev/null +++ b/Source/Core/Config/ResourceTextureSet.cs @@ -0,0 +1,125 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Data; +using System.IO; +using System.Diagnostics; +using System.Windows.Forms; +using System.Text.RegularExpressions; +using System.Collections.Specialized; + +#endregion + +namespace CodeImp.DoomBuilder.Config +{ + internal sealed class ResourceTextureSet : TextureSet, IFilledTextureSet + { + #region ================== Constants + + #endregion + + #region ================== Variables + + // Matching textures and flats + private Dictionary textures; + private Dictionary flats; + private DataLocation location; + + #endregion + + #region ================== Properties + + public ICollection Textures { get { return textures.Values; } } + public ICollection Flats { get { return flats.Values; } } + public DataLocation Location { get { return location; } } + + #endregion + + #region ================== Constructor / Destructor + + // New texture set constructor + public ResourceTextureSet(string name, DataLocation location) + { + this.name = name; + this.location = location; + this.textures = new Dictionary(); + this.flats = new Dictionary(); + } + + #endregion + + #region ================== Methods + + // Add a texture + internal void AddTexture(ImageData image) + { + if (textures.ContainsKey(image.LongName)) + General.ErrorLogger.Add(ErrorType.Warning, "Texture \"" + image.Name + "\" is double defined in resource \"" + this.Location.location + "\"."); + textures[image.LongName] = image; + } + + // Add a flat + internal void AddFlat(ImageData image) + { + if (flats.ContainsKey(image.LongName)) + General.ErrorLogger.Add(ErrorType.Warning, "Flat \"" + image.Name + "\" is double defined in resource \"" + this.Location.location + "\"."); + flats[image.LongName] = image; + } + + // Check if this set has a texture + internal bool TextureExists(ImageData image) + { + return textures.ContainsKey(image.LongName); + } + + // Check if this set has a flat + internal bool FlatExists(ImageData image) + { + return flats.ContainsKey(image.LongName); + } + + // Mix the textures and flats + internal void MixTexturesAndFlats() + { + // Make a copy of the flats only + Dictionary flatsonly = new Dictionary(flats); + + // Add textures to flats + foreach (KeyValuePair t in textures) + { + if (!flats.ContainsKey(t.Key)) + flats.Add(t.Key, t.Value); + } + + // Add flats to textures + foreach (KeyValuePair f in flatsonly) + { + if (!textures.ContainsKey(f.Key)) + textures.Add(f.Key, f.Value); + } + } + + #endregion + } +} diff --git a/Source/Core/Config/ScriptConfiguration.cs b/Source/Core/Config/ScriptConfiguration.cs new file mode 100644 index 0000000..a57380f --- /dev/null +++ b/Source/Core/Config/ScriptConfiguration.cs @@ -0,0 +1,257 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Data; +using System.IO; +using System.Diagnostics; +using System.Windows.Forms; +using CodeImp.DoomBuilder.Map; +using System.Collections.ObjectModel; + +#endregion + +namespace CodeImp.DoomBuilder.Config +{ + internal class ScriptConfiguration : IComparable + { + #region ================== Constants + + #endregion + + #region ================== Variables + + // Original configuration + private Configuration cfg; + + // Compiler settings + private CompilerInfo compiler; + private string parameters; + private string resultlump; + + // Editor settings + private string description; + private int codepage; + private string[] extensions; + private bool casesensitive; + private int insertcase; + private int lexer; + private string keywordhelp; + private string functionopen; + private string functionclose; + private string argumentdelimiter; + private string terminator; + private string functionregex; + + // Collections + private Dictionary keywords; + private Dictionary lowerkeywords; + private List constants; + private Dictionary lowerconstants; + + #endregion + + #region ================== Properties + + // Compiler settings + public CompilerInfo Compiler { get { return compiler; } } + public string Parameters { get { return parameters; } } + public string ResultLump { get { return resultlump; } } + + // Editor settings + public string Description { get { return description; } } + public int CodePage { get { return codepage; } } + public string[] Extensions { get { return extensions; } } + public bool CaseSensitive { get { return casesensitive; } } + public int InsertCase { get { return insertcase; } } + public int Lexer { get { return lexer; } } + public string KeywordHelp { get { return keywordhelp; } } + public string FunctionOpen { get { return functionopen; } } + public string FunctionClose { get { return functionclose; } } + public string ArgumentDelimiter { get { return argumentdelimiter; } } + public string Terminator { get { return terminator; } } + public string FunctionRegEx { get { return functionregex; } } + + // Collections + public ICollection Keywords { get { return keywords.Keys; } } + public ICollection Constants { get { return constants; } } + + #endregion + + #region ================== Constructor / Disposer + + // This creates the default script configuration + // that is used for documents of unknown type + internal ScriptConfiguration() + { + // Initialize + this.cfg = new Configuration(); + this.keywords = new Dictionary(); + this.constants = new List(); + this.lowerkeywords = new Dictionary(); + this.lowerconstants = new Dictionary(); + + // Settings + lexer = 1; + casesensitive = false; + codepage = 65001; + parameters = ""; + resultlump = ""; + insertcase = 0; + keywordhelp = ""; + functionopen = ""; + functionclose = ""; + argumentdelimiter = ""; + terminator = ""; + functionregex = ""; + description = "Plain text"; + extensions = new string[] { "txt" }; + } + + // Constructor + internal ScriptConfiguration(Configuration cfg) + { + string compilername; + string extensionsstring; + IDictionary dic; + + // Initialize + this.cfg = cfg; + this.keywords = new Dictionary(); + this.constants = new List(); + this.lowerkeywords = new Dictionary(); + this.lowerconstants = new Dictionary(); + + // Read settings + description = cfg.ReadSetting("description", "Untitled script"); + codepage = cfg.ReadSetting("codepage", 0); + extensionsstring = cfg.ReadSetting("extensions", ""); + compilername = cfg.ReadSetting("compiler", ""); + parameters = cfg.ReadSetting("parameters", ""); + resultlump = cfg.ReadSetting("resultlump", ""); + casesensitive = cfg.ReadSetting("casesensitive", true); + insertcase = cfg.ReadSetting("insertcase", 0); + lexer = cfg.ReadSetting("lexer", 0); + keywordhelp = cfg.ReadSetting("keywordhelp", ""); + functionopen = cfg.ReadSetting("functionopen", ""); + functionclose = cfg.ReadSetting("functionclose", ""); + argumentdelimiter = cfg.ReadSetting("argumentdelimiter", ""); + terminator = cfg.ReadSetting("terminator", ""); + functionregex = cfg.ReadSetting("functionregex", ""); + + // Make extensions array + extensions = extensionsstring.Split(','); + for (int i = 0; i < extensions.Length; i++) extensions[i] = extensions[i].Trim(); + + // Load keywords + dic = cfg.ReadSetting("keywords", new Hashtable()); + foreach (DictionaryEntry de in dic) + { + keywords.Add(de.Key.ToString(), de.Value.ToString()); + lowerkeywords.Add(de.Key.ToString().ToLowerInvariant(), de.Key.ToString()); + } + + // Load constants + dic = cfg.ReadSetting("constants", new Hashtable()); + foreach (DictionaryEntry de in dic) + { + constants.Add(de.Key.ToString()); + lowerconstants.Add(de.Key.ToString().ToLowerInvariant(), de.Key.ToString()); + } + + // Compiler specified? + if (compilername.Length > 0) + { + // Find compiler + foreach (CompilerInfo c in General.Compilers) + { + // Compiler name matches? + if (c.Name == compilername) + { + // Apply compiler + this.compiler = c; + break; + } + } + + // No compiler found? + if (this.compiler == null) throw new Exception("No such compiler defined: '" + compilername + "'"); + } + } + + #endregion + + #region ================== Methods + + // This returns the correct case for a keyword + // Returns the same keyword as the input when it cannot be found + public string GetKeywordCase(string keyword) + { + if (lowerkeywords.ContainsKey(keyword.ToLowerInvariant())) + return lowerkeywords[keyword.ToLowerInvariant()]; + else + return keyword; + } + + // This returns the correct case for a constant + // Returns the same constant as the input when it cannot be found + public string GetConstantCase(string constant) + { + if (lowerconstants.ContainsKey(constant.ToLowerInvariant())) + return lowerconstants[constant.ToLowerInvariant()]; + else + return constant; + } + + // This returns true when the given word is a keyword + public bool IsKeyword(string keyword) + { + return lowerkeywords.ContainsKey(keyword.ToLowerInvariant()); + } + + // This returns true when the given word is a contant + public bool IsConstant(string constant) + { + return lowerconstants.ContainsKey(constant.ToLowerInvariant()); + } + + // This returns the function definition for a keyword + // Returns null when no function definition exists + // NOTE: The keyword parameter is case-sensitive! + public string GetFunctionDefinition(string keyword) + { + if (keywords.ContainsKey(keyword)) + return keywords[keyword]; + else + return null; + } + + // This sorts by description + public int CompareTo(ScriptConfiguration other) + { + return string.Compare(this.description, other.description, true); + } + + #endregion + } +} diff --git a/Source/Core/Config/SectorEffectInfo.cs b/Source/Core/Config/SectorEffectInfo.cs new file mode 100644 index 0000000..44430d9 --- /dev/null +++ b/Source/Core/Config/SectorEffectInfo.cs @@ -0,0 +1,96 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Data; +using System.IO; +using System.Diagnostics; +using System.Windows.Forms; +using CodeImp.DoomBuilder.Map; + +#endregion + +namespace CodeImp.DoomBuilder.Config +{ + public class SectorEffectInfo : INumberedTitle, IComparable + { + #region ================== Constants + + #endregion + + #region ================== Variables + + // Properties + private int index; + private string title; + private bool isknown; + private bool isgeneralized; + + #endregion + + #region ================== Properties + + public int Index { get { return index; } } + public string Title { get { return title; } } + public bool IsGeneralized { get { return isgeneralized; } } + public bool IsKnown { get { return isknown; } } + public bool IsNull { get { return (index == 0); } } + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + internal SectorEffectInfo(int index, string title, bool isknown, bool isgeneralized) + { + // Initialize + this.index = index; + this.title = title; + this.isknown = isknown; + this.isgeneralized = isgeneralized; + + // We have no destructor + GC.SuppressFinalize(this); + } + + #endregion + + #region ================== Methods + + // This presents the item as string + public override string ToString() + { + return index + " - " + title; + } + + // This compares against another action info + public int CompareTo(SectorEffectInfo other) + { + if (this.index < other.index) return -1; + else if (this.index > other.index) return 1; + else return 0; + } + + #endregion + } +} diff --git a/Source/Core/Config/SkillInfo.cs b/Source/Core/Config/SkillInfo.cs new file mode 100644 index 0000000..d4e7f12 --- /dev/null +++ b/Source/Core/Config/SkillInfo.cs @@ -0,0 +1,86 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Data; +using System.IO; +using System.Diagnostics; +using System.Windows.Forms; +using CodeImp.DoomBuilder.Map; + +#endregion + +namespace CodeImp.DoomBuilder.Config +{ + public class SkillInfo : INumberedTitle, IComparable + { + #region ================== Constants + + #endregion + + #region ================== Variables + + // Properties + private int index; + private string title; + + #endregion + + #region ================== Properties + + public int Index { get { return index; } } + public string Title { get { return title; } } + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + internal SkillInfo(int index, string title) + { + // Initialize + this.index = index; + this.title = title; + } + + #endregion + + #region ================== Methods + + // This presents the item as string + public override string ToString() + { + return index + " - " + title; + } + + // This compares against another skill + public int CompareTo(SkillInfo other) + { + if (this.index < other.index) return -1; + else if (this.index > other.index) return 1; + else return 0; + } + + #endregion + } +} diff --git a/Source/Core/Config/TagType.cs b/Source/Core/Config/TagType.cs new file mode 100644 index 0000000..d2a1898 --- /dev/null +++ b/Source/Core/Config/TagType.cs @@ -0,0 +1,41 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Data; +using System.IO; +using System.Diagnostics; +using System.Windows.Forms; + +#endregion + +namespace CodeImp.DoomBuilder.Config +{ + public enum TagType + { + None = 0, + Sector = 1, + Thing = 2, + Linedef = 3 // Implement this? + } +} diff --git a/Source/Core/Config/TextureIndexInfo.cs b/Source/Core/Config/TextureIndexInfo.cs new file mode 100644 index 0000000..08e1ad5 --- /dev/null +++ b/Source/Core/Config/TextureIndexInfo.cs @@ -0,0 +1,86 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Data; +using System.IO; +using System.Diagnostics; +using System.Windows.Forms; +using CodeImp.DoomBuilder.Map; + +#endregion + +namespace CodeImp.DoomBuilder.Config +{ + public class TextureIndexInfo : INumberedTitle, IComparable + { + #region ================== Constants + + #endregion + + #region ================== Variables + + // Properties + private int index; + private string title; + + #endregion + + #region ================== Properties + + public int Index { get { return index; } } + public string Title { get { return title; } } + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + internal TextureIndexInfo(int index, string title) + { + // Initialize + this.index = index; + this.title = title; + } + + #endregion + + #region ================== Methods + + // This presents the item as string + public override string ToString() + { + return index + " - " + title; + } + + // This compares against another skill + public int CompareTo(TextureIndexInfo other) + { + if (this.index < other.index) return -1; + else if (this.index > other.index) return 1; + else return 0; + } + + #endregion + } +} diff --git a/Source/Core/Config/TextureSet.cs b/Source/Core/Config/TextureSet.cs new file mode 100644 index 0000000..92d0a6d --- /dev/null +++ b/Source/Core/Config/TextureSet.cs @@ -0,0 +1,78 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Data; +using System.IO; +using System.Diagnostics; +using System.Windows.Forms; +using System.Text.RegularExpressions; +using System.Collections.Specialized; + +#endregion + +namespace CodeImp.DoomBuilder.Config +{ + public abstract class TextureSet : IComparable + { + #region ================== Variables + + protected string name; + protected List filters; + + #endregion + + #region ================== Properties + + public string Name { get { return name; } set { name = value; } } + internal List Filters { get { return filters; } } + + #endregion + + #region ================== Constructor / Destructor + + public TextureSet() + { + this.name = "Unnamed Set"; + this.filters = new List(); + } + + #endregion + + #region ================== Methods + + // This returns the name + public override string ToString() + { + return name; + } + + // Comparer for sorting alphabetically + public int CompareTo(TextureSet other) + { + return string.Compare(this.name, other.name); + } + + #endregion + } +} diff --git a/Source/Core/Config/ThingCategory.cs b/Source/Core/Config/ThingCategory.cs new file mode 100644 index 0000000..375618e --- /dev/null +++ b/Source/Core/Config/ThingCategory.cs @@ -0,0 +1,216 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Data; +using System.IO; +using System.Diagnostics; +using System.Windows.Forms; +using CodeImp.DoomBuilder.Rendering; + +#endregion + +namespace CodeImp.DoomBuilder.Config +{ + public class ThingCategory + { + #region ================== Constants + + #endregion + + #region ================== Variables + + // Things + private List things; + + // Category properties + private string name; + private string title; + private bool sorted; + + // Thing properties for inheritance + private string sprite; + private int color; + private int arrow; + private float radius; + private float height; + private int hangs; + private int blocking; + private int errorcheck; + private bool fixedsize; + private bool absolutez; + private float spritescale; + private int palindex; // villsa + + // Disposing + private bool isdisposed = false; + + #endregion + + #region ================== Properties + + public string Name { get { return name; } } + public string Title { get { return title; } } + public string Sprite { get { return sprite; } } + public bool Sorted { get { return sorted; } } + public int Color { get { return color; } } + public int Arrow { get { return arrow; } } + public float Radius { get { return radius; } } + public float Height { get { return height; } } + public int Hangs { get { return hangs; } } + public int Blocking { get { return blocking; } } + public int ErrorCheck { get { return errorcheck; } } + public bool FixedSize { get { return fixedsize; } } + public bool IsDisposed { get { return isdisposed; } } + public bool AbsoluteZ { get { return absolutez; } } + public float SpriteScale { get { return spritescale; } } + public int PalIndex { get { return palindex; } } // villsa + public List Things { get { return things; } } + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + internal ThingCategory(string name, string title) + { + // Initialize + this.name = name; + this.title = title; + this.things = new List(); + + // Set default properties + this.sprite = ""; + this.sorted = true; + this.color = 18; + this.arrow = 1; + this.radius = 10; + this.height = 20; + this.hangs = 0; + this.blocking = 0; + this.errorcheck = 1; + this.fixedsize = false; + this.absolutez = false; + this.spritescale = 1.0f; + this.palindex = 0; // villsa + + // We have no destructor + GC.SuppressFinalize(this); + } + + // Constructor + internal ThingCategory(Configuration cfg, string name, IDictionary enums) + { + IDictionary dic; + int index; + + // Initialize + this.name = name; + this.things = new List(); + + // Read properties + this.title = cfg.ReadSetting("thingtypes." + name + ".title", ""); + this.sprite = cfg.ReadSetting("thingtypes." + name + ".sprite", ""); + this.sorted = (cfg.ReadSetting("thingtypes." + name + ".sort", 0) != 0); + this.color = cfg.ReadSetting("thingtypes." + name + ".color", 0); + this.arrow = cfg.ReadSetting("thingtypes." + name + ".arrow", 0); + this.radius = cfg.ReadSetting("thingtypes." + name + ".width", 10); + this.height = cfg.ReadSetting("thingtypes." + name + ".height", 20); + this.hangs = cfg.ReadSetting("thingtypes." + name + ".hangs", 0); + this.blocking = cfg.ReadSetting("thingtypes." + name + ".blocking", 0); + this.errorcheck = cfg.ReadSetting("thingtypes." + name + ".error", 1); + this.fixedsize = cfg.ReadSetting("thingtypes." + name + ".fixedsize", false); + this.absolutez = cfg.ReadSetting("thingtypes." + name + ".absolutez", false); + this.spritescale = cfg.ReadSetting("thingtypes." + name + ".spritescale", 1.0f); + this.palindex = cfg.ReadSetting("thingtypes." + name + ".palindex", 0); + + // Safety + if (this.radius < 4f) this.radius = 8f; + + // Go for all items in category + dic = cfg.ReadSetting("thingtypes." + name, new Hashtable()); + foreach (DictionaryEntry de in dic) + { + // Check if the item key is numeric + if (int.TryParse(de.Key.ToString(), NumberStyles.AllowLeadingWhite | NumberStyles.AllowTrailingWhite, CultureInfo.InvariantCulture, out index)) + { + // Check if the item value is a structure + if (de.Value is IDictionary) + { + // Create this thing + things.Add(new ThingTypeInfo(this, index, cfg, enums)); + } + // Check if the item value is a string + else if (de.Value is string) + { + // Interpret this as the title + things.Add(new ThingTypeInfo(this, index, de.Value.ToString())); + } + } + } + + // We have no destructor + GC.SuppressFinalize(this); + } + + // Disposer + internal void Dispose() + { + // Not already disposed? + if (!isdisposed) + { + // Clean up + things = null; + + // Done + isdisposed = true; + } + } + + #endregion + + #region ================== Methods + + // This sorts the category, if preferred + internal void SortIfNeeded() + { + if (sorted) things.Sort(); + } + + // This adds a thing to the category + internal void AddThing(ThingTypeInfo t) + { + // Add + things.Add(t); + } + + // String representation + public override string ToString() + { + return title; + } + + #endregion + } +} + diff --git a/Source/Core/Config/ThingTypeInfo.cs b/Source/Core/Config/ThingTypeInfo.cs new file mode 100644 index 0000000..037b17a --- /dev/null +++ b/Source/Core/Config/ThingTypeInfo.cs @@ -0,0 +1,311 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Data; +using System.IO; +using System.Diagnostics; +using System.Windows.Forms; +using CodeImp.DoomBuilder.ZDoom; +using CodeImp.DoomBuilder.Map; +using System.Drawing; + +#endregion + +namespace CodeImp.DoomBuilder.Config +{ + public class ThingTypeInfo : IComparable + { + #region ================== Constants + + public const int THING_BLOCKING_NONE = 0; + public const int THING_BLOCKING_FULL = 1; + public const int THING_BLOCKING_HEIGHT = 2; + public const int THING_ERROR_NONE = 0; + public const int THING_ERROR_INSIDE = 1; + public const int THING_ERROR_INSIDE_STUCK = 2; + + #endregion + + #region ================== Variables + + // Properties + private int index; + private string title; + private string sprite; + private long spritelongname; + private int color; + private bool arrow; + private float radius; + private float height; + private bool hangs; + private int blocking; + private int errorcheck; + private bool fixedsize; + private ThingCategory category; + private ArgumentInfo[] args; + private bool isknown; + private bool absolutez; + private SizeF spritescale; + private int palindex; // villsa + + #endregion + + #region ================== Properties + + public int Index { get { return index; } } + public string Title { get { return title; } } + public string Sprite { get { return sprite; } } + public long SpriteLongName { get { return spritelongname; } } + public int Color { get { return color; } } + public bool Arrow { get { return arrow; } } + public float Radius { get { return radius; } } + public float Height { get { return height; } } + public bool Hangs { get { return hangs; } } + public int Blocking { get { return blocking; } } + public int ErrorCheck { get { return errorcheck; } } + public bool FixedSize { get { return fixedsize; } } + public ThingCategory Category { get { return category; } } + public ArgumentInfo[] Args { get { return args; } } + public bool IsKnown { get { return isknown; } } + public bool IsNull { get { return (index == 0); } } + public bool AbsoluteZ { get { return absolutez; } } + public SizeF SpriteScale { get { return spritescale; } } + public int PalIndex { get { return palindex; } } // villsa + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + internal ThingTypeInfo(int index) + { + // Initialize + this.index = index; + this.category = null; + this.title = "<" + index.ToString(CultureInfo.InvariantCulture) + ">"; + this.sprite = DataManager.INTERNAL_PREFIX + "unknownthing"; + this.color = 0; + this.arrow = true; + this.radius = 10f; + this.height = 20f; + this.hangs = false; + this.blocking = 0; + this.errorcheck = 0; + this.spritescale = new SizeF(1.0f, 1.0f); + this.fixedsize = false; + this.spritelongname = long.MaxValue; + this.args = new ArgumentInfo[Linedef.NUM_ARGS]; + this.isknown = false; + this.absolutez = false; + this.palindex = 0; // villsa + + // We have no destructor + GC.SuppressFinalize(this); + } + + // Constructor + internal ThingTypeInfo(ThingCategory cat, int index, Configuration cfg, IDictionary enums) + { + string key = index.ToString(CultureInfo.InvariantCulture); + + // Initialize + this.index = index; + this.category = cat; + this.args = new ArgumentInfo[Linedef.NUM_ARGS]; + this.isknown = true; + + // Read properties + this.title = cfg.ReadSetting("thingtypes." + cat.Name + "." + key + ".title", "<" + key + ">"); + this.sprite = cfg.ReadSetting("thingtypes." + cat.Name + "." + key + ".sprite", cat.Sprite); + this.color = cfg.ReadSetting("thingtypes." + cat.Name + "." + key + ".color", cat.Color); + this.arrow = (cfg.ReadSetting("thingtypes." + cat.Name + "." + key + ".arrow", cat.Arrow) != 0); + this.radius = cfg.ReadSetting("thingtypes." + cat.Name + "." + key + ".width", cat.Radius); + this.height = cfg.ReadSetting("thingtypes." + cat.Name + "." + key + ".height", cat.Height); + this.hangs = (cfg.ReadSetting("thingtypes." + cat.Name + "." + key + ".hangs", cat.Hangs) != 0); + this.blocking = cfg.ReadSetting("thingtypes." + cat.Name + "." + key + ".blocking", cat.Blocking); + this.errorcheck = cfg.ReadSetting("thingtypes." + cat.Name + "." + key + ".error", cat.ErrorCheck); + this.fixedsize = cfg.ReadSetting("thingtypes." + cat.Name + "." + key + ".fixedsize", cat.FixedSize); + this.absolutez = cfg.ReadSetting("thingtypes." + cat.Name + "." + key + ".absolutez", cat.AbsoluteZ); + float sscale = cfg.ReadSetting("thingtypes." + cat.Name + "." + key + ".spritescale", cat.SpriteScale); + this.palindex = cfg.ReadSetting("thingtypes." + cat.Name + "." + key + ".palindex", cat.PalIndex); // villsa + + // Read the args + for (int i = 0; i < Linedef.NUM_ARGS; i++) + this.args[i] = new ArgumentInfo(cfg, "thingtypes." + cat.Name + "." + key, i, enums); + + // Safety + if (this.radius < 4f) this.radius = 8f; + + // Make long name for sprite lookup + if (this.sprite.Length <= 8) + this.spritelongname = Lump.MakeLongName(this.sprite); + else + this.spritelongname = long.MaxValue; + + // We have no destructor + GC.SuppressFinalize(this); + } + + // Constructor + public ThingTypeInfo(ThingCategory cat, int index, string title) + { + string key = index.ToString(CultureInfo.InvariantCulture); + + // Initialize + this.index = index; + this.category = cat; + this.title = title; + this.isknown = true; + this.args = new ArgumentInfo[Linedef.NUM_ARGS]; + for (int i = 0; i < Linedef.NUM_ARGS; i++) this.args[i] = new ArgumentInfo(i); + + // Read properties + this.sprite = cat.Sprite; + this.color = cat.Color; + this.arrow = (cat.Arrow != 0); + this.radius = cat.Radius; + this.height = cat.Height; + this.hangs = (cat.Hangs != 0); + this.blocking = cat.Blocking; + this.errorcheck = cat.ErrorCheck; + this.fixedsize = cat.FixedSize; + this.absolutez = cat.AbsoluteZ; + this.spritescale = new SizeF(cat.SpriteScale, cat.SpriteScale); + this.palindex = cat.PalIndex; // villsa + + // Safety + if (this.radius < 4f) this.radius = 8f; + + // Make long name for sprite lookup + if (this.sprite.Length <= 8) + this.spritelongname = Lump.MakeLongName(this.sprite); + else + this.spritelongname = long.MaxValue; + + // We have no destructor + GC.SuppressFinalize(this); + } + + // Constructor + internal ThingTypeInfo(ThingCategory cat, ActorStructure actor) + { + // Initialize + this.index = actor.DoomEdNum; + this.category = cat; + this.title = ""; + this.isknown = true; + this.args = new ArgumentInfo[Linedef.NUM_ARGS]; + for (int i = 0; i < Linedef.NUM_ARGS; i++) this.args[i] = new ArgumentInfo(i); + + // Read properties + this.sprite = cat.Sprite; + this.color = cat.Color; + this.arrow = (cat.Arrow != 0); + this.radius = cat.Radius; + this.height = cat.Height; + this.hangs = (cat.Hangs != 0); + this.blocking = cat.Blocking; + this.errorcheck = cat.ErrorCheck; + this.fixedsize = cat.FixedSize; + this.absolutez = cat.AbsoluteZ; + this.spritescale = new SizeF(cat.SpriteScale, cat.SpriteScale); + this.palindex = cat.PalIndex; // villsa + + // Safety + if (this.radius < 4f) this.radius = 8f; + + // Apply settings from actor + ModifyByDecorateActor(actor); + + // We have no destructor + GC.SuppressFinalize(this); + } + + #endregion + + #region ================== Methods + + // This updates the properties from a decorate actor + internal void ModifyByDecorateActor(ActorStructure actor) + { + // Set the title + if (actor.HasPropertyWithValue("$title")) + title = actor.GetPropertyAllValues("$title"); + else if (actor.HasPropertyWithValue("tag")) + title = actor.GetPropertyAllValues("tag"); + else if (string.IsNullOrEmpty(title)) + title = actor.ClassName; + + // Remove doublequotes from title + if ((title.Length > 2) && title.StartsWith("\"") && title.EndsWith("\"")) + title = title.Substring(1, title.Length - 2); + + // Set sprite + string suitablesprite = actor.FindSuitableSprite(); + if (!string.IsNullOrEmpty(suitablesprite)) sprite = suitablesprite; + + if (this.sprite.Length <= 8) + this.spritelongname = Lump.MakeLongName(this.sprite); + else + this.spritelongname = long.MaxValue; + + // Set sprite scale + if (actor.HasPropertyWithValue("scale")) + { + float scale = actor.GetPropertyValueFloat("scale", 0); + this.spritescale = new SizeF(scale, scale); + } + else + { + if (actor.HasPropertyWithValue("xscale")) + this.spritescale.Width = actor.GetPropertyValueFloat("xscale", 0); + + if (actor.HasPropertyWithValue("yscale")) + this.spritescale.Height = actor.GetPropertyValueFloat("yscale", 0); + } + + // Size + if (actor.HasPropertyWithValue("radius")) radius = actor.GetPropertyValueInt("radius", 0); + if (actor.HasPropertyWithValue("height")) height = actor.GetPropertyValueInt("height", 0); + + // Safety + if (this.radius < 4f) this.radius = 8f; + if (this.spritescale.Width <= 0.0f) this.spritescale.Width = 1.0f; + if (this.spritescale.Height <= 0.0f) this.spritescale.Height = 1.0f; + + // Options + hangs = actor.GetFlagValue("spawnceiling", hangs); + int blockvalue = (blocking > 0) ? blocking : 2; + blocking = actor.GetFlagValue("solid", (blocking != 0)) ? blockvalue : 0; + } + + // This is used for sorting + public int CompareTo(ThingTypeInfo other) + { + return string.Compare(this.title, other.title, true); + } + + #endregion + } +} diff --git a/Source/Core/Config/ThingsFlagsCompare.cs b/Source/Core/Config/ThingsFlagsCompare.cs new file mode 100644 index 0000000..538d4eb --- /dev/null +++ b/Source/Core/Config/ThingsFlagsCompare.cs @@ -0,0 +1,316 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Map; + +#endregion + +namespace CodeImp.DoomBuilder.Config +{ + //mxd + public class ThingFlagsCompareGroup + { + public readonly string Name; + public readonly bool IsOptional; // When set to true, group flags won't be considered as required for a thing to show up ingame by CheckUnusedThings error check and ThingFlagsCompare.CheckThingEditFormFlags() method. + public readonly Dictionary Flags; + + public ThingFlagsCompareGroup(Configuration cfg, string name) + { + Name = name; + Flags = new Dictionary(); + IsOptional = cfg.ReadSetting("thingflagscompare." + name + ".optional", false); + + IDictionary dic = cfg.ReadSetting("thingflagscompare." + name, new Hashtable()); + foreach (DictionaryEntry de in dic) + { + if (de.Value != null && !(de.Value is IDictionary)) continue; // flag either has no value, or is defined as block + string flag = de.Key.ToString(); + + // Duplicate flags check + if (Flags.ContainsKey(flag)) + General.ErrorLogger.Add(ErrorType.Warning, "ThingFlagsCompare flag \"" + flag + "\" is double defined in the \"" + name + "\" group"); + + Flags[flag] = new ThingFlagsCompare(cfg, name, flag); + } + } + + // Compares flags group of the two things. + public ThingFlagsCompareResult Compare(Thing t1, Thing t2) + { + ThingFlagsCompareResult result = new ThingFlagsCompareResult(); + foreach (ThingFlagsCompare tfc in Flags.Values) + { + // Current flag doesn't overlap when required flag does not overlap + if (!string.IsNullOrEmpty(tfc.RequiredFlag) && !GetFlag(tfc.RequiredFlag).Compare(t1, t2)) + { + result.Result = -1; + continue; + } + + // Compare current flag + bool flagoverlaps = tfc.Compare(t1, t2); + + // Ignore this group when whole group doens't match or required flag is not set + if (!flagoverlaps && tfc.IgnoreGroupWhenUnset) return new ThingFlagsCompareResult { Result = 0 }; + + // If current flag overlaps, check IgnoredGroup and RequiredGroup settings + if (flagoverlaps) + { + result.Result = 1; + + foreach (string s in tfc.IgnoredGroups) + { + if (!result.IgnoredGroups.Contains(s)) result.IgnoredGroups.Add(s); + } + + if (tfc.RequiredGroups.Count > 0) + { + foreach (string s in tfc.RequiredGroups) + { + if (result.IgnoredGroups.Contains(s)) result.IgnoredGroups.Remove(s); + if (!result.RequiredGroups.Contains(s)) result.RequiredGroups.Add(s); + } + } + } + } + + return result; + } + + public ThingFlagsCompare GetFlag(string flag) + { + // Check our flags + if (Flags.ContainsKey(flag)) return Flags[flag]; + + // Check other groups + foreach (ThingFlagsCompareGroup group in General.Map.Config.ThingFlagsCompare.Values) + { + if (group != this && group.Flags.ContainsKey(flag)) return group.Flags[flag]; + } + + // Fial... + return null; + } + } + + //mxd + public class ThingFlagsCompareResult + { + public readonly HashSet IgnoredGroups; + public readonly HashSet RequiredGroups; + + // -1 if group does not overlap + // 0 if group should be ignored + // 1 if group overlaps + public int Result; + + public ThingFlagsCompareResult() + { + Result = -1; + IgnoredGroups = new HashSet(); + RequiredGroups = new HashSet(); + } + } + + public class ThingFlagsCompare + { + private enum CompareMethod + { + Equal, + And + }; + + #region ================== Constants + + #endregion + + #region ================== Variables + + private readonly string flag; + private readonly HashSet requiredgroups; //mxd. This flag only works if at least one flag is set in the "requiredgroup" + private readonly HashSet ignoredgroups; //mxd. If this flag is set, flags from ignoredgroup can be... well... ignored! + private string requiredflag; //mxd. This flag only works if requiredflag is set. + private readonly bool ingnorethisgroupwhenunset; //mxd + private readonly CompareMethod comparemethod; + private readonly bool invert; + private readonly char[] comma = new[] { ',' }; + + #endregion + + #region ================== Properties + + public string Flag { get { return flag; } } + public HashSet RequiredGroups { get { return requiredgroups; } } //mxd + public HashSet IgnoredGroups { get { return ignoredgroups; } } //mxd + public string RequiredFlag { get { return requiredflag; } internal set { requiredflag = value; } } //mxd + public bool IgnoreGroupWhenUnset { get { return ingnorethisgroupwhenunset; } } //mxd + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + public ThingFlagsCompare(Configuration cfg, string group, string flag) + { + string cfgpath = "thingflagscompare." + group + "." + flag; + this.flag = flag; + + string cm = cfg.ReadSetting(cfgpath + ".comparemethod", "and"); + switch (cm) + { + default: + General.ErrorLogger.Add(ErrorType.Warning, "Unrecognized value \"" + cm + "\" for comparemethod in " + cfgpath + " in game configuration " + cfg.ReadSetting("game", "") + ". Defaulting to \"and\"."); + goto case "and"; + case "and": + comparemethod = CompareMethod.And; + break; + case "equal": + comparemethod = CompareMethod.Equal; + break; + } + + invert = cfg.ReadSetting(cfgpath + ".invert", false); + + //mxd + requiredgroups = new HashSet(); + string[] requiredgroupsarr = cfg.ReadSetting(cfgpath + ".requiredgroups", string.Empty).Split(comma, StringSplitOptions.RemoveEmptyEntries); + foreach (string s in requiredgroupsarr) + if (!requiredgroups.Contains(s)) requiredgroups.Add(s); + + //mxd + ignoredgroups = new HashSet(); + string[] ignoredgroupsarr = cfg.ReadSetting(cfgpath + ".ignoredgroups", string.Empty).Split(comma, StringSplitOptions.RemoveEmptyEntries); + foreach (string s in ignoredgroupsarr) + if (!ignoredgroups.Contains(s)) ignoredgroups.Add(s); + + requiredflag = cfg.ReadSetting(cfgpath + ".requiredflag", string.Empty); //mxd + ingnorethisgroupwhenunset = cfg.ReadSetting(cfgpath + ".ingnorethisgroupwhenunset", false); //mxd + + // We have no destructor + GC.SuppressFinalize(this); + } + + #endregion + + #region ================== Methods + + // Compares the flag of the two things. + public bool Compare(Thing t1, Thing t2) + { + //mxd. Get flags + bool t1flag = (invert ? !t1.IsFlagSet(flag) : t1.IsFlagSet(flag)); + bool t2flag = (invert ? !t2.IsFlagSet(flag) : t2.IsFlagSet(flag)); + + //mxd. Ignore the flag when ingnorethisgroupwhenunset is set and both flags are unset + if (!t1flag && !t2flag && ingnorethisgroupwhenunset) return false; + + //mxd. Compare them + switch (comparemethod) + { + case CompareMethod.And: return t1flag && t2flag; + case CompareMethod.Equal: return t1flag == t2flag; + default: throw new NotImplementedException("Unknown compare method!"); + } + } + + //mxd + public static List CheckFlags(HashSet flags) + { + Dictionary> flagspergroup = new Dictionary>(General.Map.Config.ThingFlagsCompare.Count); + HashSet ignoredgroups = new HashSet(); + + // Gather flags per group + foreach (KeyValuePair group in General.Map.Config.ThingFlagsCompare) + { + flagspergroup.Add(group.Key, new HashSet()); + + foreach (ThingFlagsCompare flag in group.Value.Flags.Values) + { + if (IsFlagSet(flags, flag.flag, flag.invert) && + (string.IsNullOrEmpty(flag.requiredflag) || IsFlagSet(flags, flag.requiredflag, group.Value.GetFlag(flag.requiredflag).invert))) + { + flagspergroup[group.Key].Add(flag.Flag); + foreach (string s in flag.ignoredgroups) + if (!ignoredgroups.Contains(s)) ignoredgroups.Add(s); + } + else if (flag.ingnorethisgroupwhenunset) + { + flagspergroup.Remove(group.Key); + break; + } + } + } + + // Check required dependancies + foreach (KeyValuePair> group in flagspergroup) + { + foreach (string flag in group.Value) + { + foreach (string s in General.Map.Config.ThingFlagsCompare[group.Key].Flags[flag].requiredgroups) + { + if (ignoredgroups.Contains(s)) ignoredgroups.Remove(s); + } + } + } + + // Get rid of ignoredgroups + foreach (string s in ignoredgroups) flagspergroup.Remove(s); + + // Return message + List result = new List(); + + foreach (KeyValuePair> group in flagspergroup) + { + if (group.Value.Count == 0 && !General.Map.Config.ThingFlagsCompare[group.Key].IsOptional) + { + switch (group.Key) + { + case "skills": + result.Add("Thing is not used in any skill level."); + break; + case "gamemodes": + result.Add("Thing is not used in any game mode."); + break; + case "classes": + result.Add("Thing is not used by any class."); + break; + default: + result.Add("At least one \"" + group.Key + "\" flag should be set."); + break; + } + } + } + + return result; + } + + //mxd + private static bool IsFlagSet(HashSet flags, string flag, bool invert) + { + bool result = flags.Contains(flag); + return (invert ? !result : result); + } + + #endregion + } +} + diff --git a/Source/Core/Config/UniversalFieldInfo.cs b/Source/Core/Config/UniversalFieldInfo.cs new file mode 100644 index 0000000..c178a9a --- /dev/null +++ b/Source/Core/Config/UniversalFieldInfo.cs @@ -0,0 +1,113 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Data; +using System.IO; +using System.Diagnostics; +using System.Windows.Forms; +using CodeImp.DoomBuilder.Map; + +#endregion + +namespace CodeImp.DoomBuilder.Config +{ + public class UniversalFieldInfo : IComparable + { + #region ================== Constants + + #endregion + + #region ================== Variables + + // Properties + private string name; + private int type; + private object defaultvalue; + private EnumList enumlist; + + #endregion + + #region ================== Properties + + public string Name { get { return name; } } + public int Type { get { return type; } } + public object Default { get { return defaultvalue; } } + public EnumList Enum { get { return enumlist; } } + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + internal UniversalFieldInfo(string path, string name, Configuration cfg, IDictionary enums) + { + string setting = "universalfields." + path + "." + name; + + // Initialize + this.name = name.ToLowerInvariant(); + + // Read type + this.type = cfg.ReadSetting(setting + ".type", 0); + this.defaultvalue = cfg.ReadSettingObject(setting + ".default", null); + + // Read enum + object enumsetting = cfg.ReadSettingObject(setting + ".enum", null); + if (enumsetting != null) + { + // Reference to existing enums list? + if (enumsetting is string) + { + // Link to it + enumlist = enums[enumsetting.ToString()]; + } + else if (enumsetting is IDictionary) + { + // Make list + enumlist = new EnumList(enumsetting as IDictionary); + } + } + + // We have no destructor + GC.SuppressFinalize(this); + } + + #endregion + + #region ================== Methods + + // This presents the item as string + public override string ToString() + { + return name; + } + + // This compares against another field + public int CompareTo(UniversalFieldInfo other) + { + return string.Compare(this.name, other.name); + } + + #endregion + } +} diff --git a/Source/Core/Controls/ActionSelectorControl.Designer.cs b/Source/Core/Controls/ActionSelectorControl.Designer.cs new file mode 100644 index 0000000..a48f7ee --- /dev/null +++ b/Source/Core/Controls/ActionSelectorControl.Designer.cs @@ -0,0 +1,101 @@ +namespace CodeImp.DoomBuilder.Controls +{ + partial class ActionSelectorControl + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.number = new CodeImp.DoomBuilder.Controls.AutoSelectTextbox(); + this.list = new System.Windows.Forms.ComboBox(); + this.numberpanel = new System.Windows.Forms.Panel(); + this.numberpanel.SuspendLayout(); + this.SuspendLayout(); + // + // number + // + this.number.BorderStyle = System.Windows.Forms.BorderStyle.None; + this.number.ImeMode = System.Windows.Forms.ImeMode.Off; + this.number.Location = new System.Drawing.Point(3, 1); + this.number.Name = "number"; + this.number.Size = new System.Drawing.Size(43, 13); + this.number.TabIndex = 0; + this.number.Text = "402"; + this.number.TextAlign = System.Windows.Forms.HorizontalAlignment.Right; + this.number.TextChanged += new System.EventHandler(this.number_TextChanged); + this.number.KeyDown += new System.Windows.Forms.KeyEventHandler(this.number_KeyDown); + this.number.KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.number_KeyPress); + // + // list + // + this.list.DrawMode = System.Windows.Forms.DrawMode.OwnerDrawFixed; + this.list.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.list.FormattingEnabled = true; + this.list.IntegralHeight = false; + this.list.Location = new System.Drawing.Point(57, 0); + this.list.MaxDropDownItems = 15; + this.list.Name = "list"; + this.list.Size = new System.Drawing.Size(251, 21); + this.list.TabIndex = 1; + this.list.TabStop = false; + this.list.DrawItem += new System.Windows.Forms.DrawItemEventHandler(this.list_DrawItem); + this.list.SelectionChangeCommitted += new System.EventHandler(this.list_SelectionChangeCommitted); + this.list.DropDownClosed += new System.EventHandler(this.list_DropDownClosed); + // + // numberpanel + // + this.numberpanel.BackColor = System.Drawing.SystemColors.Window; + this.numberpanel.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D; + this.numberpanel.Controls.Add(this.number); + this.numberpanel.Location = new System.Drawing.Point(0, 0); + this.numberpanel.Name = "numberpanel"; + this.numberpanel.Size = new System.Drawing.Size(53, 21); + this.numberpanel.TabIndex = 0; + // + // ActionSelectorControl + // + this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; + this.BackColor = System.Drawing.SystemColors.Control; + this.Controls.Add(this.numberpanel); + this.Controls.Add(this.list); + this.Cursor = System.Windows.Forms.Cursors.Default; + this.Name = "ActionSelectorControl"; + this.Size = new System.Drawing.Size(382, 21); + this.Layout += new System.Windows.Forms.LayoutEventHandler(this.ActionSelectorControl_Layout); + this.Resize += new System.EventHandler(this.ActionSelectorControl_Resize); + this.numberpanel.ResumeLayout(false); + this.numberpanel.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private CodeImp.DoomBuilder.Controls.AutoSelectTextbox number; + private System.Windows.Forms.ComboBox list; + private System.Windows.Forms.Panel numberpanel; + } +} diff --git a/Source/Core/Controls/ActionSelectorControl.cs b/Source/Core/Controls/ActionSelectorControl.cs new file mode 100644 index 0000000..2d27751 --- /dev/null +++ b/Source/Core/Controls/ActionSelectorControl.cs @@ -0,0 +1,275 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Text; +using System.Windows.Forms; +using Microsoft.Win32; +using System.Drawing.Drawing2D; +using CodeImp.DoomBuilder.Config; + +#endregion + +namespace CodeImp.DoomBuilder.Controls +{ + public partial class ActionSelectorControl : UserControl + { + // Events + public event EventHandler ValueChanges; + + // Variables + private List generalizedcategories; + private bool controlpressed = false; + private bool isamacro = false; // villsa + + // Constants + private const string NUMBER_SEPERATOR = "\t"; + + // Properties + public bool Macro { get { return isamacro; } set { isamacro = value; } } // villsa + public bool Empty { get { return (number.Text.Length == 0); } set { if (value) number.Text = ""; } } + public int Value { get { return GetValue(); } set { number.Text = value.ToString(); } } + public List GeneralizedCategories { get { return generalizedcategories; } set { generalizedcategories = value; } } + + // Constructor + public ActionSelectorControl() + { + // Initialize + InitializeComponent(); + } + + // This returns the numeric value + public int GetValue() + { + int val = 0; + + if (number.Text.Length > 0) + { + try + { + val = Convert.ToInt32(number.Text); + } + catch (Exception e) { } + } + + return val; + } + + // This clears all information + public void ClearInfo() + { + list.Items.Clear(); + } + + // This adds information to display + public void AddInfo(INumberedTitle[] infolist) + { + // Add to list + list.Items.AddRange(infolist); + } + + // Resized + private void ActionSelectorControl_Resize(object sender, EventArgs e) + { + list.Width = ClientRectangle.Width - list.Left; + ClientSize = new Size(ClientSize.Width, list.Height); + } + + // Layout change + private void ActionSelectorControl_Layout(object sender, LayoutEventArgs e) + { + ActionSelectorControl_Resize(sender, e); + } + + // This draws an item in the combobox + private void list_DrawItem(object sender, DrawItemEventArgs e) + { + INumberedTitle item; + Brush displaybrush = SystemBrushes.WindowText; + Brush backbrush = SystemBrushes.Window; + string displayname = ""; + int intnumber = 0; + + // Only when running + if (!this.DesignMode) + { + // Unknow item? + if (e.Index < 0) + { + // Grayed + displaybrush = new SolidBrush(SystemColors.GrayText); + backbrush = new SolidBrush(SystemColors.Window); + + // Try getting integral number + int.TryParse(number.Text, out intnumber); + + // Check what to display + if (number.Text.Length == 0) + displayname = ""; + else if (intnumber == 0) + displayname = "None"; + /*else if (isamacro == true) // villsa + { + displayname = "Macro"; + number.Text = (intnumber - 255).ToString(); + }*/ + else if ((generalizedcategories != null) && GameConfiguration.IsGeneralized(intnumber, generalizedcategories)) + displayname = "Generalized (" + General.Map.Config.GetGeneralizedActionCategory(intnumber) + ")"; + else + { + displayname = "Unknown"; + } + } + // In the display part of the combobox? + else if ((e.State & DrawItemState.ComboBoxEdit) != 0) + { + // Show without number + item = (INumberedTitle)list.Items[e.Index]; + + /*if (isamacro == true) // villsa + displayname = "Macro"; + else*/ + displayname = item.Title.Trim(); + + // Determine colors to use + if (item.Index == 0) + { + // Grayed + displaybrush = new SolidBrush(SystemColors.GrayText); + backbrush = new SolidBrush(SystemColors.Window); + } + else + { + // Normal color + displaybrush = new SolidBrush(list.ForeColor); + backbrush = new SolidBrush(SystemColors.Window); + } + } + else + { + // Use number and description + item = (INumberedTitle)list.Items[e.Index]; + + /*if (isamacro == true) // villsa + displayname = "Macro"; + else + */ + displayname = item.Index + NUMBER_SEPERATOR + item.Title; + + // Determine colors to use + if ((e.State & DrawItemState.Focus) != 0) + { + displaybrush = new SolidBrush(SystemColors.HighlightText); + backbrush = new SolidBrush(SystemColors.Highlight); + } + else + { + displaybrush = new SolidBrush(list.ForeColor); + backbrush = new SolidBrush(SystemColors.Window); + } + } + + } + + // Draw item + e.Graphics.FillRectangle(backbrush, e.Bounds); + e.Graphics.DrawString(displayname, list.Font, displaybrush, e.Bounds.X, e.Bounds.Y); + } + + // List closed + private void list_DropDownClosed(object sender, EventArgs e) + { + // Focus to number box + number.SelectAll(); + number.Focus(); + } + + // Number changes + private void number_TextChanged(object sender, EventArgs e) + { + int itemindex = -1; + INumberedTitle item; + + // Not nothing? + if (number.Text.Length > 0) + { + // Find the index in the list + for (int i = 0; i < list.Items.Count; i++) + { + // This is the item we're looking for? + item = (INumberedTitle)list.Items[i]; + if (item.Index.ToString() == number.Text) + { + // Found it + itemindex = i; + break; + } + } + } + + // Select item + if (list.SelectedIndex != itemindex) list.SelectedIndex = itemindex; + list.Refresh(); + + // Raise change event + if (ValueChanges != null) ValueChanges(this, EventArgs.Empty); + } + + // Keys pressed in number box + private void number_KeyDown(object sender, KeyEventArgs e) + { + controlpressed = e.Control; + + // Allow CTRL+X, CTRL+C and CTRL+V + if (controlpressed && ((e.KeyCode == Keys.X) || (e.KeyCode == Keys.C) || (e.KeyCode == Keys.V))) return; + + // Not numeric or control key? + if (((e.KeyValue < 48) || (e.KeyValue > 57)) && + (e.KeyCode != Keys.Back) && (e.KeyCode != Keys.Left) && + (e.KeyCode != Keys.Right) && (e.KeyCode != Keys.Delete)) + { + // Cancel this + e.Handled = true; + } + } + + // Keys pressed in number box + private void number_KeyPress(object sender, KeyPressEventArgs e) + { + // Allow CTRL+X, CTRL+C and CTRL+V + if (controlpressed && ((e.KeyChar == '\u0018') || (e.KeyChar == '\u0003') || (e.KeyChar == '\u0016'))) return; + + // Not numeric or control key? + if (((e.KeyChar < 48) || (e.KeyChar > 57)) && (e.KeyChar != 8)) + { + // Cancel this + e.Handled = true; + } + } + + // Selection made + private void list_SelectionChangeCommitted(object sender, EventArgs e) + { + INumberedTitle item = (INumberedTitle)list.SelectedItem; + number.Text = item.Index.ToString(); + } + } +} diff --git a/Source/Core/Controls/ActionSelectorControl.resx b/Source/Core/Controls/ActionSelectorControl.resx new file mode 100644 index 0000000..41f9539 --- /dev/null +++ b/Source/Core/Controls/ActionSelectorControl.resx @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + True + + + True + + + True + + + True + + \ No newline at end of file diff --git a/Source/Core/Controls/AngleControl.Designer.cs b/Source/Core/Controls/AngleControl.Designer.cs new file mode 100644 index 0000000..5d5290f --- /dev/null +++ b/Source/Core/Controls/AngleControl.Designer.cs @@ -0,0 +1,173 @@ +namespace CodeImp.DoomBuilder.Controls +{ + partial class AngleControl + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.button0 = new System.Windows.Forms.RadioButton(); + this.button1 = new System.Windows.Forms.RadioButton(); + this.button2 = new System.Windows.Forms.RadioButton(); + this.button3 = new System.Windows.Forms.RadioButton(); + this.button4 = new System.Windows.Forms.RadioButton(); + this.button5 = new System.Windows.Forms.RadioButton(); + this.button6 = new System.Windows.Forms.RadioButton(); + this.button7 = new System.Windows.Forms.RadioButton(); + this.SuspendLayout(); + // + // button0 + // + this.button0.AutoSize = true; + this.button0.BackColor = System.Drawing.Color.Transparent; + this.button0.Location = new System.Drawing.Point(62, 33); + this.button0.Name = "button0"; + this.button0.Padding = new System.Windows.Forms.Padding(3); + this.button0.Size = new System.Drawing.Size(20, 19); + this.button0.TabIndex = 0; + this.button0.UseVisualStyleBackColor = false; + this.button0.CheckedChanged += new System.EventHandler(this.button_CheckedChanged); + // + // button1 + // + this.button1.AutoSize = true; + this.button1.BackColor = System.Drawing.Color.Transparent; + this.button1.Location = new System.Drawing.Point(54, 11); + this.button1.Name = "button1"; + this.button1.Padding = new System.Windows.Forms.Padding(3); + this.button1.Size = new System.Drawing.Size(20, 19); + this.button1.TabIndex = 1; + this.button1.UseVisualStyleBackColor = false; + this.button1.CheckedChanged += new System.EventHandler(this.button_CheckedChanged); + // + // button2 + // + this.button2.AutoSize = true; + this.button2.BackColor = System.Drawing.Color.Transparent; + this.button2.Location = new System.Drawing.Point(33, 3); + this.button2.Name = "button2"; + this.button2.Padding = new System.Windows.Forms.Padding(3); + this.button2.Size = new System.Drawing.Size(20, 19); + this.button2.TabIndex = 2; + this.button2.UseVisualStyleBackColor = false; + this.button2.CheckedChanged += new System.EventHandler(this.button_CheckedChanged); + // + // button3 + // + this.button3.AutoSize = true; + this.button3.BackColor = System.Drawing.Color.Transparent; + this.button3.Location = new System.Drawing.Point(11, 11); + this.button3.Name = "button3"; + this.button3.Padding = new System.Windows.Forms.Padding(3); + this.button3.Size = new System.Drawing.Size(20, 19); + this.button3.TabIndex = 3; + this.button3.UseVisualStyleBackColor = false; + this.button3.CheckedChanged += new System.EventHandler(this.button_CheckedChanged); + // + // button4 + // + this.button4.AutoSize = true; + this.button4.BackColor = System.Drawing.Color.Transparent; + this.button4.Location = new System.Drawing.Point(3, 33); + this.button4.Name = "button4"; + this.button4.Padding = new System.Windows.Forms.Padding(3); + this.button4.Size = new System.Drawing.Size(20, 19); + this.button4.TabIndex = 4; + this.button4.UseVisualStyleBackColor = false; + this.button4.CheckedChanged += new System.EventHandler(this.button_CheckedChanged); + // + // button5 + // + this.button5.AutoSize = true; + this.button5.BackColor = System.Drawing.Color.Transparent; + this.button5.Location = new System.Drawing.Point(11, 55); + this.button5.Name = "button5"; + this.button5.Padding = new System.Windows.Forms.Padding(3); + this.button5.Size = new System.Drawing.Size(20, 19); + this.button5.TabIndex = 5; + this.button5.UseVisualStyleBackColor = false; + this.button5.CheckedChanged += new System.EventHandler(this.button_CheckedChanged); + // + // button6 + // + this.button6.AutoSize = true; + this.button6.BackColor = System.Drawing.Color.Transparent; + this.button6.Location = new System.Drawing.Point(33, 63); + this.button6.Name = "button6"; + this.button6.Padding = new System.Windows.Forms.Padding(3); + this.button6.Size = new System.Drawing.Size(20, 19); + this.button6.TabIndex = 6; + this.button6.UseVisualStyleBackColor = false; + this.button6.CheckedChanged += new System.EventHandler(this.button_CheckedChanged); + // + // button7 + // + this.button7.AutoSize = true; + this.button7.BackColor = System.Drawing.Color.Transparent; + this.button7.Location = new System.Drawing.Point(54, 55); + this.button7.Name = "button7"; + this.button7.Padding = new System.Windows.Forms.Padding(3); + this.button7.Size = new System.Drawing.Size(20, 19); + this.button7.TabIndex = 7; + this.button7.UseVisualStyleBackColor = false; + this.button7.CheckedChanged += new System.EventHandler(this.button_CheckedChanged); + // + // AngleControl + // + this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; + this.BackColor = System.Drawing.SystemColors.Control; + this.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D; + this.Controls.Add(this.button7); + this.Controls.Add(this.button6); + this.Controls.Add(this.button5); + this.Controls.Add(this.button4); + this.Controls.Add(this.button3); + this.Controls.Add(this.button2); + this.Controls.Add(this.button1); + this.Controls.Add(this.button0); + this.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.Name = "AngleControl"; + this.Size = new System.Drawing.Size(84, 84); + this.Paint += new System.Windows.Forms.PaintEventHandler(this.AngleControl_Paint); + this.Layout += new System.Windows.Forms.LayoutEventHandler(this.AngleControl_Layout); + this.Resize += new System.EventHandler(this.AngleControl_Resize); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.RadioButton button0; + private System.Windows.Forms.RadioButton button1; + private System.Windows.Forms.RadioButton button2; + private System.Windows.Forms.RadioButton button3; + private System.Windows.Forms.RadioButton button4; + private System.Windows.Forms.RadioButton button5; + private System.Windows.Forms.RadioButton button6; + private System.Windows.Forms.RadioButton button7; + } +} diff --git a/Source/Core/Controls/AngleControl.cs b/Source/Core/Controls/AngleControl.cs new file mode 100644 index 0000000..7a37f7d --- /dev/null +++ b/Source/Core/Controls/AngleControl.cs @@ -0,0 +1,182 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Text; +using System.Globalization; +using System.Windows.Forms; +using CodeImp.DoomBuilder.Actions; +using CodeImp.DoomBuilder.Geometry; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Editing; +using System.Drawing.Drawing2D; + +#endregion + +namespace CodeImp.DoomBuilder.Controls +{ + /// + /// Control which allows you to click on one of the buttons to select a rotation by 45 degrees. + /// + public partial class AngleControl : UserControl + { + #region ================== Constants + + private const float LINE_THICKNESS = 3f; + + #endregion + + #region ================== Events + + public event EventHandler ValueChanged; + public event EventHandler ButtonClicked; + + #endregion + + #region ================== Variables + + // Buttons + private RadioButton[] buttons; + + // Result + private int angle; + private bool settingangle; + + #endregion + + #region ================== Properties + + public int Value { get { return angle; } set { SetAngle(value, true); } } + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + public AngleControl() + { + // Initialize + InitializeComponent(); + + // Make array from buttons + buttons = new RadioButton[8]; + buttons[0] = button0; + buttons[1] = button1; + buttons[2] = button2; + buttons[3] = button3; + buttons[4] = button4; + buttons[5] = button5; + buttons[6] = button6; + buttons[7] = button7; + } + + #endregion + + #region ================== Interface + + // Size changed + protected override void OnClientSizeChanged(EventArgs e) + { + base.OnClientSizeChanged(e); + AngleControl_Resize(this, e); + } + + // Layout changed + private void AngleControl_Layout(object sender, LayoutEventArgs e) + { + AngleControl_Resize(sender, e); + } + + // Size changed + private void AngleControl_Resize(object sender, EventArgs e) + { + //this.Size = new Size(84, 84); + } + + // Redraw the control + private void AngleControl_Paint(object sender, PaintEventArgs e) + { + float rad = Angle2D.DegToRad((float)angle); + e.Graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; + e.Graphics.InterpolationMode = InterpolationMode.High; + e.Graphics.SmoothingMode = SmoothingMode.HighQuality; + e.Graphics.Clear(this.BackColor); + Pen linepen = new Pen(SystemColors.ControlText, LINE_THICKNESS); + PointF start = new PointF((float)this.Size.Width * 0.5f, (float)this.Size.Height * 0.5f); + float line_length = (float)this.Size.Width * 0.26f; + if ((rad >= 0) && (rad < 360)) + { + PointF end = new PointF(start.X + (float)Math.Sin(rad + Angle2D.PIHALF) * line_length, + start.Y + (float)Math.Cos(rad + Angle2D.PIHALF) * line_length); + e.Graphics.DrawLine(linepen, start, end); + } + else + { + e.Graphics.DrawLine(linepen, start, start); + } + } + + #endregion + + #region ================== Control + + // This sets an angle manually + private void SetAngle(int newangle, bool changebuttons) + { + bool changed; + + // Normalize and apply angle + changed = (newangle != angle); + angle = newangle; + + // Check if it matches an angle from the buttons + if (changebuttons) + { + settingangle = true; + for (int i = 0; i < 8; i++) + buttons[i].Checked = (angle == i * 45); + settingangle = false; + } + + // Redraw + this.Invalidate(); + + // Raise event + if ((ValueChanged != null) && changed) ValueChanged(this, EventArgs.Empty); + } + + // When checked state of a button changes + private void button_CheckedChanged(object sender, EventArgs e) + { + if (!settingangle) + { + // Check if we can get the angle from one of the buttons + for (int i = 0; i < 8; i++) + if (buttons[i].Checked) SetAngle(i * 45, false); + + // Raise event + if (ButtonClicked != null) ButtonClicked(this, EventArgs.Empty); + } + } + + #endregion + } +} diff --git a/Source/Core/Controls/AngleControl.resx b/Source/Core/Controls/AngleControl.resx new file mode 100644 index 0000000..24bc645 --- /dev/null +++ b/Source/Core/Controls/AngleControl.resx @@ -0,0 +1,147 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + \ No newline at end of file diff --git a/Source/Core/Controls/ArgumentBox.Designer.cs b/Source/Core/Controls/ArgumentBox.Designer.cs new file mode 100644 index 0000000..5d4b01a --- /dev/null +++ b/Source/Core/Controls/ArgumentBox.Designer.cs @@ -0,0 +1,94 @@ +namespace CodeImp.DoomBuilder.Controls +{ + partial class ArgumentBox + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.combobox = new System.Windows.Forms.ComboBox(); + this.button = new System.Windows.Forms.Button(); + this.scrollbuttons = new System.Windows.Forms.VScrollBar(); + this.SuspendLayout(); + // + // combobox + // + this.combobox.DropDownWidth = 130; + this.combobox.Location = new System.Drawing.Point(0, 1); + this.combobox.Name = "combobox"; + this.combobox.Size = new System.Drawing.Size(149, 22); + this.combobox.TabIndex = 0; + this.combobox.Validating += new System.ComponentModel.CancelEventHandler(this.combobox_Validating); + this.combobox.TextChanged += new System.EventHandler(this.combobox_TextChanged); + // + // button + // + this.button.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.button.Image = global::CodeImp.DoomBuilder.Properties.Resources.treeview; + this.button.ImageAlign = System.Drawing.ContentAlignment.BottomCenter; + this.button.Location = new System.Drawing.Point(153, 0); + this.button.Name = "button"; + this.button.Padding = new System.Windows.Forms.Padding(0, 0, 1, 2); + this.button.Size = new System.Drawing.Size(30, 24); + this.button.TabIndex = 1; + this.button.UseVisualStyleBackColor = true; + this.button.Visible = false; + this.button.Click += new System.EventHandler(this.button_Click); + // + // scrollbuttons + // + this.scrollbuttons.LargeChange = 10000; + this.scrollbuttons.Location = new System.Drawing.Point(186, -1); + this.scrollbuttons.Maximum = 10000; + this.scrollbuttons.Minimum = -10000; + this.scrollbuttons.Name = "scrollbuttons"; + this.scrollbuttons.Size = new System.Drawing.Size(18, 24); + this.scrollbuttons.TabIndex = 2; + this.scrollbuttons.Visible = false; + this.scrollbuttons.ValueChanged += new System.EventHandler(this.scrollbuttons_ValueChanged); + // + // ArgumentBox + // + this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; + this.Controls.Add(this.scrollbuttons); + this.Controls.Add(this.button); + this.Controls.Add(this.combobox); + this.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.Name = "ArgumentBox"; + this.Size = new System.Drawing.Size(268, 64); + this.Layout += new System.Windows.Forms.LayoutEventHandler(this.ArgumentBox_Layout); + this.Resize += new System.EventHandler(this.ArgumentBox_Resize); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.ComboBox combobox; + private System.Windows.Forms.Button button; + private System.Windows.Forms.VScrollBar scrollbuttons; + } +} diff --git a/Source/Core/Controls/ArgumentBox.cs b/Source/Core/Controls/ArgumentBox.cs new file mode 100644 index 0000000..c4d9973 --- /dev/null +++ b/Source/Core/Controls/ArgumentBox.cs @@ -0,0 +1,286 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Text; +using System.Windows.Forms; +using Microsoft.Win32; +using CodeImp.DoomBuilder.Config; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Types; +using System.Globalization; + +#endregion + +namespace CodeImp.DoomBuilder.Controls +{ + /// + /// Control to accept a linedef action argument. + /// + public partial class ArgumentBox : UserControl + { + #region ================== Variables + + private TypeHandler typehandler; + private bool ignorebuttonchange = false; + + #endregion + + #region ================== Properties + + #endregion + + #region ================== Constructor + + // Constructor + public ArgumentBox() + { + // Initialize + InitializeComponent(); + scrollbuttons.Value = 0; + combobox.MouseWheel += combobox_MouseWheel; + } + + #endregion + + #region ================== Events + + // When control resizes + private void ArgumentBox_Resize(object sender, EventArgs e) + { + if (button.Visible) + combobox.Width = ClientRectangle.Width - button.Width - 2; + else if (scrollbuttons.Visible) + combobox.Width = ClientRectangle.Width - scrollbuttons.Width - 2; + else + combobox.Width = ClientRectangle.Width; + + button.Left = ClientRectangle.Width - button.Width; + scrollbuttons.Left = ClientRectangle.Width - scrollbuttons.Width; + Height = button.Height; + } + + // When control layout is aplied + private void ArgumentBox_Layout(object sender, LayoutEventArgs e) + { + ArgumentBox_Resize(sender, e); + } + + // When the entered value needs to be validated + private void combobox_Validating(object sender, CancelEventArgs e) + { + string str = combobox.Text.Trim().ToLowerInvariant(); + str = str.TrimStart('+', '-'); + int num; + + // Anything in the box? + if (combobox.Text.Trim().Length > 0) + { + // Prefixed? + if (CheckIsRelative()) + { + // Try parsing to number + if (!int.TryParse(str, NumberStyles.Integer, CultureInfo.CurrentCulture, out num)) + { + // Invalid relative number + combobox.SelectedItem = null; + combobox.Text = ""; + } + } + else + { + // Set the value. The type handler will validate it + // and make the best possible choice. + typehandler.SetValue(combobox.Text); + combobox.SelectedItem = null; + combobox.Text = typehandler.GetStringValue(); + } + } + } + + // When browse button is clicked + private void button_Click(object sender, EventArgs e) + { + // Browse for a value + typehandler.Browse(this); + combobox.SelectedItem = null; + combobox.Text = typehandler.GetStringValue(); + combobox.Focus(); + combobox.SelectAll(); + } + + // Text changes + private void combobox_TextChanged(object sender, EventArgs e) + { + scrollbuttons.Enabled = !CheckIsRelative(); + } + + // Mouse wheel used + private void combobox_MouseWheel(object sender, MouseEventArgs e) + { + if (scrollbuttons.Visible) + { + if (e.Delta < 0) + scrollbuttons.Value += 1; + else if (e.Delta > 0) + scrollbuttons.Value -= 1; + } + } + + // Scroll buttons clicked + private void scrollbuttons_ValueChanged(object sender, EventArgs e) + { + if (!ignorebuttonchange) + { + ignorebuttonchange = true; + if (!CheckIsRelative()) + { + typehandler.SetValue(combobox.Text); + int newvalue = GetResult(0) - scrollbuttons.Value; + if (newvalue < 0) newvalue = 0; + combobox.Text = newvalue.ToString(); + combobox_Validating(sender, new CancelEventArgs()); + } + scrollbuttons.Value = 0; + ignorebuttonchange = false; + } + } + + #endregion + + #region ================== Methods + + // This sets up the control for a specific argument + public void Setup(ArgumentInfo arginfo) + { + int oldvalue = 0; + + // Get the original value + if (typehandler != null) oldvalue = typehandler.GetIntValue(); + + // Get the type handler + typehandler = General.Types.GetArgumentHandler(arginfo); + + // Clear combobox + combobox.SelectedItem = null; + combobox.Items.Clear(); + + // Check if this supports enumerated options + if (typehandler.IsEnumerable) + { + // Show the combobox + button.Visible = false; + scrollbuttons.Visible = false; + combobox.DropDownStyle = ComboBoxStyle.DropDown; + combobox.Items.AddRange(typehandler.GetEnumList().ToArray()); + } + // Check if browsable + else if (typehandler.IsBrowseable) + { + // Show the button + button.Visible = true; + scrollbuttons.Visible = false; + combobox.DropDownStyle = ComboBoxStyle.Simple; + } + else + { + // Show textbox with scroll buttons + button.Visible = false; + scrollbuttons.Visible = true; + combobox.DropDownStyle = ComboBoxStyle.Simple; + } + + // Setup layout + ArgumentBox_Resize(this, EventArgs.Empty); + + // Re-apply value + SetValue(oldvalue); + } + + // This sets the value + public void SetValue(int value) + { + typehandler.SetValue(value); + combobox.SelectedItem = null; + combobox.Text = typehandler.GetStringValue(); + combobox_Validating(this, new CancelEventArgs()); + } + + // This clears the value + public void ClearValue() + { + typehandler.SetValue(""); + combobox.SelectedItem = null; + combobox.Text = ""; + } + + // This checks if the number is relative + public bool CheckIsRelative() + { + // Prefixed with ++ or --? + return (combobox.Text.Trim().StartsWith("++") || combobox.Text.Trim().StartsWith("--")); + } + + // This returns the selected value + public int GetResult(int original) + { + int result = 0; + + // Strip prefixes + string str = combobox.Text.Trim().ToLowerInvariant(); + str = str.TrimStart('+', '-'); + int num = original; + + // Anything in the box? + if (combobox.Text.Trim().Length > 0) + { + // Prefixed with ++? + if (combobox.Text.Trim().StartsWith("++")) + { + // Add number to original + if (!int.TryParse(str, out num)) num = 0; + result = original + num; + } + // Prefixed with --? + else if (combobox.Text.Trim().StartsWith("--")) + { + // Subtract number from original + if (!int.TryParse(str, out num)) num = 0; + result = original - num; + } + else + { + // Return the value + result = typehandler.GetIntValue(); + } + } + else + { + // Just return the original + result = original; + } + + return General.Clamp(result, General.Map.FormatInterface.MinArgument, General.Map.FormatInterface.MaxArgument); + } + + #endregion + } +} diff --git a/Source/Core/Controls/ArgumentBox.resx b/Source/Core/Controls/ArgumentBox.resx new file mode 100644 index 0000000..9f6cd25 --- /dev/null +++ b/Source/Core/Controls/ArgumentBox.resx @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + True + + + True + + + True + + + True + + \ No newline at end of file diff --git a/Source/Core/Controls/AutoSelectTextbox.cs b/Source/Core/Controls/AutoSelectTextbox.cs new file mode 100644 index 0000000..9883599 --- /dev/null +++ b/Source/Core/Controls/AutoSelectTextbox.cs @@ -0,0 +1,94 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Text; +using System.Globalization; +using System.Windows.Forms; +using CodeImp.DoomBuilder.Actions; +using CodeImp.DoomBuilder.Geometry; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Editing; + +#endregion + +namespace CodeImp.DoomBuilder.Controls +{ + public class AutoSelectTextbox : TextBox + { + #region ================== Constants + + #endregion + + #region ================== Variables + + private int eventcount = 0; + + #endregion + + #region ================== Properties + + #endregion + + #region ================== Constructor / Disposer + + #endregion + + #region ================== Methods + + // When gaining focus + protected override void OnGotFocus(EventArgs e) + { + base.OnGotFocus(e); + + // Reset counter + eventcount = 0; + } + + // When losing focus + protected override void OnLostFocus(EventArgs e) + { + base.OnLostFocus(e); + + // Reset counter + eventcount = 0; + } + + // When mouse pressed down + protected override void OnMouseDown(MouseEventArgs e) + { + base.OnMouseDown(e); + + // Select all text when this mouseclick gives focus + if (eventcount == 0) this.SelectAll(); + eventcount++; + } + + // When key is pressed + protected override void OnKeyDown(KeyEventArgs e) + { + base.OnKeyDown(e); + eventcount++; + } + + #endregion + } +} diff --git a/Source/Core/Controls/ButtonsNumericTextbox.Designer.cs b/Source/Core/Controls/ButtonsNumericTextbox.Designer.cs new file mode 100644 index 0000000..99d8fb5 --- /dev/null +++ b/Source/Core/Controls/ButtonsNumericTextbox.Designer.cs @@ -0,0 +1,79 @@ +namespace CodeImp.DoomBuilder.Controls +{ + partial class ButtonsNumericTextbox + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.buttons = new System.Windows.Forms.VScrollBar(); + this.textbox = new CodeImp.DoomBuilder.Controls.NumericTextbox(); + this.SuspendLayout(); + // + // buttons + // + this.buttons.LargeChange = 10000; + this.buttons.Location = new System.Drawing.Point(163, 0); + this.buttons.Maximum = 10000; + this.buttons.Minimum = -10000; + this.buttons.Name = "buttons"; + this.buttons.Size = new System.Drawing.Size(18, 24); + this.buttons.TabIndex = 1; + this.buttons.ValueChanged += new System.EventHandler(this.buttons_ValueChanged); + // + // textbox + // + this.textbox.AllowDecimal = false; + this.textbox.AllowNegative = false; + this.textbox.AllowRelative = false; + this.textbox.ImeMode = System.Windows.Forms.ImeMode.Off; + this.textbox.Location = new System.Drawing.Point(0, 2); + this.textbox.Name = "textbox"; + this.textbox.Size = new System.Drawing.Size(160, 20); + this.textbox.TabIndex = 0; + this.textbox.TextChanged += new System.EventHandler(this.textbox_TextChanged); + this.textbox.KeyDown += new System.Windows.Forms.KeyEventHandler(this.textbox_KeyDown); + // + // ButtonsNumericTextbox + // + this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; + this.Controls.Add(this.buttons); + this.Controls.Add(this.textbox); + this.Name = "ButtonsNumericTextbox"; + this.Size = new System.Drawing.Size(289, 68); + this.Layout += new System.Windows.Forms.LayoutEventHandler(this.ClickableNumericTextbox_Layout); + this.Resize += new System.EventHandler(this.ClickableNumericTextbox_Resize); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private NumericTextbox textbox; + private System.Windows.Forms.VScrollBar buttons; + } +} diff --git a/Source/Core/Controls/ButtonsNumericTextbox.cs b/Source/Core/Controls/ButtonsNumericTextbox.cs new file mode 100644 index 0000000..0f608f8 --- /dev/null +++ b/Source/Core/Controls/ButtonsNumericTextbox.cs @@ -0,0 +1,198 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Text; +using System.Globalization; +using System.Windows.Forms; +using CodeImp.DoomBuilder.Actions; +using CodeImp.DoomBuilder.Geometry; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Editing; + +#endregion + +namespace CodeImp.DoomBuilder.Controls +{ + [Designer(typeof(ButtonsNumericTextboxDesigner))] + public partial class ButtonsNumericTextbox : UserControl + { + #region ================== Events + + public event EventHandler WhenTextChanged; + public event EventHandler WhenButtonsClicked; + public event EventHandler WhenEnterPressed; + + #endregion + + #region ================== Variables + + private bool ignorebuttonchange = false; + private StepsList steps = null; + private int stepsize = 1; + + #endregion + + #region ================== Properties + + public bool AllowDecimal { get { return textbox.AllowDecimal; } set { textbox.AllowDecimal = value; } } + public bool AllowNegative { get { return textbox.AllowNegative; } set { textbox.AllowNegative = value; } } + public bool AllowRelative { get { return textbox.AllowRelative; } set { textbox.AllowRelative = value; } } + public int ButtonStep { get { return stepsize; } set { stepsize = value; } } + public string Text { get { return textbox.Text; } set { textbox.Text = value; } } + internal NumericTextbox Textbox { get { return textbox; } } + public StepsList StepValues { get { return steps; } set { steps = value; } } + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + public ButtonsNumericTextbox() + { + InitializeComponent(); + buttons.Value = 0; + textbox.MouseWheel += textbox_MouseWheel; + } + + #endregion + + #region ================== Interface + + // Client size changes + protected override void OnClientSizeChanged(EventArgs e) + { + base.OnClientSizeChanged(e); + ClickableNumericTextbox_Resize(this, e); + } + + // Layout changes + private void ClickableNumericTextbox_Layout(object sender, LayoutEventArgs e) + { + ClickableNumericTextbox_Resize(sender, e); + } + + // Control resizes + private void ClickableNumericTextbox_Resize(object sender, EventArgs e) + { + buttons.Height = textbox.Height + 4; + textbox.Width = ClientRectangle.Width - buttons.Width - 2; + buttons.Left = textbox.Width + 2; + this.Height = buttons.Height; + } + + // Text in textbox changes + private void textbox_TextChanged(object sender, EventArgs e) + { + if (WhenTextChanged != null) WhenTextChanged(sender, e); + buttons.Enabled = !textbox.CheckIsRelative(); + } + + // Buttons changed + private void buttons_ValueChanged(object sender, EventArgs e) + { + if (!ignorebuttonchange) + { + ignorebuttonchange = true; + if (!textbox.CheckIsRelative()) + { + if (steps != null) + { + if (buttons.Value < 0) + textbox.Text = steps.GetNextHigher(textbox.GetResult(0)).ToString(); + else if (buttons.Value > 0) + textbox.Text = steps.GetNextLower(textbox.GetResult(0)).ToString(); + } + else if (textbox.AllowDecimal) + { + float newvalue = textbox.GetResultFloat(0.0f) - (float)(buttons.Value * stepsize); + if ((newvalue < 0.0f) && !textbox.AllowNegative) newvalue = 0.0f; + textbox.Text = newvalue.ToString(); + } + else + { + int newvalue = textbox.GetResult(0) - (buttons.Value * stepsize); + if ((newvalue < 0) && !textbox.AllowNegative) newvalue = 0; + textbox.Text = newvalue.ToString(); + } + } + + buttons.Value = 0; + + if (WhenButtonsClicked != null) + WhenButtonsClicked(this, EventArgs.Empty); + + ignorebuttonchange = false; + } + } + + // Mouse wheel used + private void textbox_MouseWheel(object sender, MouseEventArgs e) + { + if (steps != null) + { + if (e.Delta > 0) + textbox.Text = steps.GetNextHigher(textbox.GetResult(0)).ToString(); + else if (e.Delta < 0) + textbox.Text = steps.GetNextLower(textbox.GetResult(0)).ToString(); + } + else + { + if (e.Delta < 0) + buttons.Value += 1; + else if (e.Delta > 0) + buttons.Value -= 1; + } + } + + // Key pressed in textbox + private void textbox_KeyDown(object sender, KeyEventArgs e) + { + // Enter key? + if ((e.KeyData == Keys.Enter) && (WhenEnterPressed != null)) + WhenEnterPressed(this, EventArgs.Empty); + } + + #endregion + + #region ================== Methods + + // This checks if the number is relative + public bool CheckIsRelative() + { + return textbox.CheckIsRelative(); + } + + // This determines the result value + public int GetResult(int original) + { + return textbox.GetResult(original); + } + + // This determines the result value + public float GetResultFloat(float original) + { + return textbox.GetResultFloat(original); + } + + #endregion + } +} diff --git a/Source/Core/Controls/ButtonsNumericTextbox.resx b/Source/Core/Controls/ButtonsNumericTextbox.resx new file mode 100644 index 0000000..88a53bc --- /dev/null +++ b/Source/Core/Controls/ButtonsNumericTextbox.resx @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + True + + + True + + + True + + \ No newline at end of file diff --git a/Source/Core/Controls/ButtonsNumericTextboxDesigner.cs b/Source/Core/Controls/ButtonsNumericTextboxDesigner.cs new file mode 100644 index 0000000..52c314c --- /dev/null +++ b/Source/Core/Controls/ButtonsNumericTextboxDesigner.cs @@ -0,0 +1,75 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.ComponentModel; +using System.ComponentModel.Design; +using System.Drawing; +using System.Text; +using System.Globalization; +using System.Windows.Forms; +using System.Windows.Forms.Design.Behavior; +using CodeImp.DoomBuilder.Actions; +using CodeImp.DoomBuilder.Geometry; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Editing; +using System.Windows.Forms.Design; + +#endregion + +namespace CodeImp.DoomBuilder.Controls +{ + // Designer for ButtonsNumericTextbox + internal class ButtonsNumericTextboxDesigner : ControlDesigner + { + public override IList SnapLines + { + get + { + IList snaplines = base.SnapLines; + + ButtonsNumericTextbox control = base.Control as ButtonsNumericTextbox; + if (control == null) { return snaplines; } + + IDesigner designer = TypeDescriptor.CreateDesigner(control.Textbox, typeof(IDesigner)); + if (designer == null) { return snaplines; } + designer.Initialize(control.Textbox); + + using (designer) + { + ControlDesigner boxdesigner = designer as ControlDesigner; + if (boxdesigner == null) { return snaplines; } + + // Add the baseline and right snap lines from the textbox + foreach (SnapLine line in boxdesigner.SnapLines) + { + if (line.SnapLineType == SnapLineType.Baseline) + snaplines.Add(new SnapLine(line.SnapLineType, line.Offset + control.Textbox.Top, line.Filter, line.Priority)); + + if (line.SnapLineType == SnapLineType.Right) + snaplines.Add(new SnapLine(line.SnapLineType, line.Offset + control.Textbox.Left, line.Filter, line.Priority)); + } + } + + return snaplines; + } + } + } +} diff --git a/Source/Core/Controls/CheckboxArrayControl.Designer.cs b/Source/Core/Controls/CheckboxArrayControl.Designer.cs new file mode 100644 index 0000000..02e95b6 --- /dev/null +++ b/Source/Core/Controls/CheckboxArrayControl.Designer.cs @@ -0,0 +1,51 @@ +namespace CodeImp.DoomBuilder.Controls +{ + partial class CheckboxArrayControl + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.SuspendLayout(); + // + // CheckboxArrayControl + // + this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; + this.AutoScroll = true; + this.DoubleBuffered = true; + this.Name = "CheckboxArrayControl"; + this.Size = new System.Drawing.Size(361, 163); + this.Paint += new System.Windows.Forms.PaintEventHandler(this.CheckboxArrayControl_Paint); + this.Layout += new System.Windows.Forms.LayoutEventHandler(this.CheckboxArrayControl_Layout); + this.ResumeLayout(false); + + } + + #endregion + + + } +} diff --git a/Source/Core/Controls/CheckboxArrayControl.cs b/Source/Core/Controls/CheckboxArrayControl.cs new file mode 100644 index 0000000..1b21a39 --- /dev/null +++ b/Source/Core/Controls/CheckboxArrayControl.cs @@ -0,0 +1,135 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Text; +using System.Windows.Forms; +using Microsoft.Win32; +using System.Drawing.Drawing2D; + +#endregion + +namespace CodeImp.DoomBuilder.Controls +{ + public partial class CheckboxArrayControl : UserControl + { + // Constants + private const int SPACING_Y = 1; + + // Variables + private List checkboxes; + private int columns; + + // Properties + public List Checkboxes { get { return checkboxes; } } + public int Columns { get { return columns; } set { columns = value; } } + + // Constructor + public CheckboxArrayControl() + { + // Initialize + InitializeComponent(); + + // Setup + checkboxes = new List(); + } + + // This adds a checkbox + public CheckBox Add(string text, object tag) + { + // Make new checkbox + CheckBox c = new CheckBox(); + c.AutoSize = true; + //c.FlatStyle = FlatStyle.System; + c.UseVisualStyleBackColor = true; + c.Text = text; + c.Tag = tag; + + // Add to list + this.Controls.Add(c); + checkboxes.Add(c); + + // Return checkbox + return c; + } + + // This positions the checkboxes + public void PositionCheckboxes() + { + int boxheight = 0; + int columnlength; + int columnwidth; + int row = 0; + int col = 0; + + // Checks + if (columns < 1) return; + if (checkboxes.Count < 1) return; + + // Calculate column width + columnwidth = this.ClientSize.Width / columns; + + // Check what the biggest checkbox height is + foreach (CheckBox c in checkboxes) if (c.Height > boxheight) boxheight = c.Height; + + // Check what the preferred column length is + columnlength = 1 + (int)Math.Floor((float)(this.ClientSize.Height - boxheight) / (float)(boxheight + SPACING_Y)); + + // When not all items fit with the preferred column length + // we have to extend the column length to make it fit + if ((int)Math.Ceiling((float)checkboxes.Count / (float)columnlength) > columns) + { + // Make a column length which works for all items + columnlength = (int)Math.Ceiling((float)checkboxes.Count / (float)columns); + } + + // Go for all items + foreach (CheckBox c in checkboxes) + { + // Position checkbox + c.Location = new Point(col * columnwidth, row * boxheight + (row - 1) * SPACING_Y + SPACING_Y); + + // Next position + if (++row == columnlength) + { + row = 0; + col++; + } + } + } + + // When layout must change + private void CheckboxArrayControl_Layout(object sender, LayoutEventArgs e) + { + PositionCheckboxes(); + } + + private void CheckboxArrayControl_Paint(object sender, PaintEventArgs e) + { + if (this.DesignMode) + { + Pen p = new Pen(SystemColors.ControlDark, 1); + p.DashStyle = DashStyle.Dash; + e.Graphics.DrawRectangle(p, 0, 0, this.ClientRectangle.Width - 1, this.ClientRectangle.Height - 1); + } + } + } +} diff --git a/Source/Core/Controls/CheckboxArrayControl.resx b/Source/Core/Controls/CheckboxArrayControl.resx new file mode 100644 index 0000000..19dc0dd --- /dev/null +++ b/Source/Core/Controls/CheckboxArrayControl.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/Source/Core/Controls/ColorControl.Designer.cs b/Source/Core/Controls/ColorControl.Designer.cs new file mode 100644 index 0000000..e8b2c46 --- /dev/null +++ b/Source/Core/Controls/ColorControl.Designer.cs @@ -0,0 +1,101 @@ +namespace CodeImp.DoomBuilder.Controls +{ + partial class ColorControl + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.label = new System.Windows.Forms.Label(); + this.panel = new System.Windows.Forms.Panel(); + this.button = new System.Windows.Forms.Button(); + this.dialog = new System.Windows.Forms.ColorDialog(); + this.SuspendLayout(); + // + // label + // + this.label.Location = new System.Drawing.Point(-3, 0); + this.label.Name = "label"; + this.label.Size = new System.Drawing.Size(175, 23); + this.label.TabIndex = 0; + this.label.Text = "Color name:"; + this.label.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; + // + // panel + // + this.panel.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(255)))), ((int)(((byte)(128)))), ((int)(((byte)(0))))); + this.panel.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D; + this.panel.Location = new System.Drawing.Point(178, 0); + this.panel.Name = "panel"; + this.panel.Size = new System.Drawing.Size(27, 23); + this.panel.TabIndex = 1; + // + // button + // + this.button.BackColor = System.Drawing.SystemColors.Control; + this.button.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Center; + this.button.FlatStyle = System.Windows.Forms.FlatStyle.Popup; + this.button.Image = global::CodeImp.DoomBuilder.Properties.Resources.ColorPick; + this.button.Location = new System.Drawing.Point(322, 0); + this.button.Name = "button"; + this.button.Padding = new System.Windows.Forms.Padding(0, 0, 2, 3); + this.button.Size = new System.Drawing.Size(26, 23); + this.button.TabIndex = 2; + this.button.UseVisualStyleBackColor = false; + this.button.MouseMove += new System.Windows.Forms.MouseEventHandler(this.button_MouseMove); + this.button.Click += new System.EventHandler(this.button_Click); + this.button.MouseDown += new System.Windows.Forms.MouseEventHandler(this.button_MouseDown); + this.button.MouseUp += new System.Windows.Forms.MouseEventHandler(this.button_MouseUp); + // + // dialog + // + this.dialog.FullOpen = true; + // + // ColorControl + // + this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; + this.BackColor = System.Drawing.Color.Transparent; + this.Controls.Add(this.button); + this.Controls.Add(this.panel); + this.Controls.Add(this.label); + this.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.MaximumSize = new System.Drawing.Size(10000, 23); + this.MinimumSize = new System.Drawing.Size(100, 23); + this.Name = "ColorControl"; + this.Size = new System.Drawing.Size(348, 23); + this.Resize += new System.EventHandler(this.ColorControl_Resize); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.Label label; + private System.Windows.Forms.Panel panel; + private System.Windows.Forms.Button button; + private System.Windows.Forms.ColorDialog dialog; + } +} diff --git a/Source/Core/Controls/ColorControl.cs b/Source/Core/Controls/ColorControl.cs new file mode 100644 index 0000000..a85ea23 --- /dev/null +++ b/Source/Core/Controls/ColorControl.cs @@ -0,0 +1,101 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Text; +using System.Windows.Forms; +using Microsoft.Win32; +using CodeImp.DoomBuilder.Config; +using CodeImp.DoomBuilder.Rendering; + +#endregion + +namespace CodeImp.DoomBuilder.Controls +{ + internal partial class ColorControl : UserControl + { + // Constructor + public ColorControl() + { + // Initialize + InitializeComponent(); + } + + // Properties + public string Label { get { return label.Text; } set { label.Text = value; } } + public PixelColor Color { get { return PixelColor.FromColor(panel.BackColor); } set { panel.BackColor = System.Drawing.Color.FromArgb(value.ToInt()); } } + + // Button clicked + private void button_Click(object sender, EventArgs e) + { + // Mouse up first + button_MouseUp(sender, new MouseEventArgs(MouseButtons.Left, 1, 0, 0, 0)); + + // Show color dialog + dialog.Color = panel.BackColor; + if (dialog.ShowDialog(this.ParentForm) == DialogResult.OK) + { + // Apply new color + panel.BackColor = dialog.Color; + } + } + + // Resized + private void ColorControl_Resize(object sender, EventArgs e) + { + try + { + button.Left = ClientSize.Width - button.Width; + panel.Left = ClientSize.Width - button.Width - panel.Width - 3; + label.Left = 0; + label.Width = panel.Left; + } + catch (Exception) { } + } + + // Mouse pressed on button + private void button_MouseDown(object sender, MouseEventArgs e) + { + // This moves the image 1 pixel to right-bottom + if (e.Button == MouseButtons.Left) + button.Padding = new Padding(1, 0, 1, 1); + } + + // Mouse released on button + private void button_MouseUp(object sender, MouseEventArgs e) + { + // This moves the image 1 pixel to normal position + if (e.Button == MouseButtons.Left) + button.Padding = new Padding(0, 0, 2, 3); + + // Lose focus + panel.Focus(); + } + + // Mouse moves over button + private void button_MouseMove(object sender, MouseEventArgs e) + { + // Outside button rect? + if ((e.X < 0) || (e.X >= button.Width) || (e.Y < 0) || (e.Y >= button.Height)) + button.Padding = new Padding(0, 0, 2, 3); + } + } +} diff --git a/Source/Core/Controls/ColorControl.resx b/Source/Core/Controls/ColorControl.resx new file mode 100644 index 0000000..f2f0ce0 --- /dev/null +++ b/Source/Core/Controls/ColorControl.resx @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + \ No newline at end of file diff --git a/Source/Core/Controls/ColorControlSector.Designer.cs b/Source/Core/Controls/ColorControlSector.Designer.cs new file mode 100644 index 0000000..2e192df --- /dev/null +++ b/Source/Core/Controls/ColorControlSector.Designer.cs @@ -0,0 +1,114 @@ +namespace CodeImp.DoomBuilder.Controls +{ + partial class ColorControlSector + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.label = new System.Windows.Forms.Label(); + this.panel = new System.Windows.Forms.Panel(); + this.button = new System.Windows.Forms.Button(); + this.dialog = new System.Windows.Forms.ColorDialog(); + this.Hexbox = new System.Windows.Forms.TextBox(); + this.SuspendLayout(); + // + // label + // + this.label.Location = new System.Drawing.Point(-3, 0); + this.label.Name = "label"; + this.label.Size = new System.Drawing.Size(175, 23); + this.label.TabIndex = 0; + this.label.Text = "Color name:"; + this.label.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; + // + // panel + // + this.panel.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(255)))), ((int)(((byte)(128)))), ((int)(((byte)(0))))); + this.panel.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D; + this.panel.Location = new System.Drawing.Point(178, 0); + this.panel.Name = "panel"; + this.panel.Size = new System.Drawing.Size(27, 23); + this.panel.TabIndex = 1; + // + // button + // + this.button.BackColor = System.Drawing.SystemColors.Control; + this.button.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Center; + this.button.FlatStyle = System.Windows.Forms.FlatStyle.Popup; + this.button.Image = global::CodeImp.DoomBuilder.Properties.Resources.ColorPick; + this.button.Location = new System.Drawing.Point(233, 0); + this.button.Name = "button"; + this.button.Padding = new System.Windows.Forms.Padding(0, 0, 2, 3); + this.button.Size = new System.Drawing.Size(26, 23); + this.button.TabIndex = 2; + this.button.UseVisualStyleBackColor = false; + this.button.Click += new System.EventHandler(this.button_Click); + this.button.MouseDown += new System.Windows.Forms.MouseEventHandler(this.button_MouseDown); + this.button.MouseMove += new System.Windows.Forms.MouseEventHandler(this.button_MouseMove); + this.button.MouseUp += new System.Windows.Forms.MouseEventHandler(this.button_MouseUp); + // + // dialog + // + this.dialog.FullOpen = true; + // + // Hexbox + // + this.Hexbox.Font = new System.Drawing.Font("Arial", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.Hexbox.Location = new System.Drawing.Point(265, 0); + this.Hexbox.Name = "Hexbox"; + this.Hexbox.Size = new System.Drawing.Size(72, 21); + this.Hexbox.TabIndex = 3; + this.Hexbox.TextChanged += new System.EventHandler(this.Hexbox_TextChanged); + // + // ColorControlSector + // + this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; + this.BackColor = System.Drawing.Color.Transparent; + this.Controls.Add(this.Hexbox); + this.Controls.Add(this.button); + this.Controls.Add(this.panel); + this.Controls.Add(this.label); + this.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.MaximumSize = new System.Drawing.Size(10000, 23); + this.MinimumSize = new System.Drawing.Size(100, 23); + this.Name = "ColorControlSector"; + this.Size = new System.Drawing.Size(352, 23); + this.Resize += new System.EventHandler(this.ColorControlSector_Resize); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Label label; + private System.Windows.Forms.Panel panel; + private System.Windows.Forms.Button button; + private System.Windows.Forms.ColorDialog dialog; + private System.Windows.Forms.TextBox Hexbox; + } +} diff --git a/Source/Core/Controls/ColorControlSector.cs b/Source/Core/Controls/ColorControlSector.cs new file mode 100644 index 0000000..5e10bee --- /dev/null +++ b/Source/Core/Controls/ColorControlSector.cs @@ -0,0 +1,131 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Text; +using System.Windows.Forms; +using Microsoft.Win32; +using CodeImp.DoomBuilder.Config; +using CodeImp.DoomBuilder.Rendering; + +#endregion + +namespace CodeImp.DoomBuilder.Controls +{ + internal partial class ColorControlSector : UserControl + { + // Constructor + public ColorControlSector() + { + // Initialize + InitializeComponent(); + } + + // Properties + public string Label { get { return label.Text; } set { label.Text = value; } } + public PixelColor Color { get { return PixelColor.FromColor(panel.BackColor); } set { panel.BackColor = System.Drawing.Color.FromArgb(value.ToInt()); Hexbox.Text = Color.ToHex(); } } + + // Button clicked + private void button_Click(object sender, EventArgs e) + { + // Mouse up first + button_MouseUp(sender, new MouseEventArgs(MouseButtons.Left, 1, 0, 0, 0)); + + // Show color dialog + dialog.Color = panel.BackColor; + if (dialog.ShowDialog(this.ParentForm) == DialogResult.OK) + { + // Apply new color + //panel.BackColor = dialog.Color; + Color = PixelColor.FromColor(dialog.Color); + } + } + + // Textbox contents changed, validate and pass to panel + private void Hexbox_TextChanged(object sender, EventArgs e) + { + //force uppercase, strip leading # + Hexbox.Text = Hexbox.Text.Replace("#", "").ToUpper(); + string TempStr = Hexbox.Text; + int maxLength = Math.Min(TempStr.Length, 6); + if (TempStr.Length == 6) + { + try + { + PixelColor tempColor = PixelColor.FromHex(TempStr); + panel.BackColor = tempColor.ToColor(); + } + catch + { + MessageBox.Show("Invalid Hexadecimal", "Doom Builder", MessageBoxButtons.OK, MessageBoxIcon.Error); + //reset to default + Hexbox.Text = "808080"; + } + } + //truncate to 6 digits + else if (TempStr.Length > 6) + { + Hexbox.Text = TempStr.Substring(0, maxLength); + } + } + + // Resized + private void ColorControlSector_Resize(object sender, EventArgs e) + { + try + { + Hexbox.Left = ClientSize.Width - Hexbox.Width; + button.Left = ClientSize.Width - button.Width - Hexbox.Width - 3; + panel.Left = ClientSize.Width - button.Width - panel.Width - Hexbox.Width - 6; + label.Left = 0; + label.Width = panel.Left; + } + catch (Exception) { } + } + + // Mouse pressed on button + private void button_MouseDown(object sender, MouseEventArgs e) + { + // This moves the image 1 pixel to right-bottom + if (e.Button == MouseButtons.Left) + button.Padding = new Padding(1, 0, 1, 1); + } + + // Mouse released on button + private void button_MouseUp(object sender, MouseEventArgs e) + { + // This moves the image 1 pixel to normal position + if (e.Button == MouseButtons.Left) + button.Padding = new Padding(0, 0, 2, 3); + + // Lose focus + panel.Focus(); + } + + // Mouse moves over button + private void button_MouseMove(object sender, MouseEventArgs e) + { + // Outside button rect? + if ((e.X < 0) || (e.X >= button.Width) || (e.Y < 0) || (e.Y >= button.Height)) + button.Padding = new Padding(0, 0, 2, 3); + } + } +} diff --git a/Source/Core/Controls/ColorControlSector.resx b/Source/Core/Controls/ColorControlSector.resx new file mode 100644 index 0000000..f2f0ce0 --- /dev/null +++ b/Source/Core/Controls/ColorControlSector.resx @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + \ No newline at end of file diff --git a/Source/Core/Controls/Docker.cs b/Source/Core/Controls/Docker.cs new file mode 100644 index 0000000..8ca9386 --- /dev/null +++ b/Source/Core/Controls/Docker.cs @@ -0,0 +1,82 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Text; +using System.Windows.Forms; +using Microsoft.Win32; +using System.Diagnostics; +using CodeImp.DoomBuilder.Actions; +using CodeImp.DoomBuilder.Data; +using CodeImp.DoomBuilder.Config; +using CodeImp.DoomBuilder.Rendering; +using System.Drawing.Imaging; +using System.Drawing.Drawing2D; +using SlimDX.Direct3D9; +using SlimDX; + +#endregion + +namespace CodeImp.DoomBuilder.Controls +{ + public class Docker + { + #region ================== Variables + + private string shortname; + private string fullname; + private string title; + private Control control; + + #endregion + + #region ================== Variables + + public string Name { get { return shortname; } } + internal string FullName { get { return fullname; } } + public string Title { get { return title; } } + public Control Control { get { return control; } } + + #endregion + + #region ================== Constructor + + // Constructor + public Docker(string name, string title, Control control) + { + this.shortname = name; + this.title = title; + this.control = control; + } + + #endregion + + #region ================== Methods + + // This makes the full name + internal void MakeFullName(string prefix) + { + fullname = prefix + "_" + shortname; + } + + #endregion + } +} diff --git a/Source/Core/Controls/DockersControl.Designer.cs b/Source/Core/Controls/DockersControl.Designer.cs new file mode 100644 index 0000000..bf184b6 --- /dev/null +++ b/Source/Core/Controls/DockersControl.Designer.cs @@ -0,0 +1,94 @@ +namespace CodeImp.DoomBuilder.Controls +{ + partial class DockersControl + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.splitter = new CodeImp.DoomBuilder.Controls.TransparentPanel(); + this.tabs = new CodeImp.DoomBuilder.Controls.DockersTabsControl(); + this.SuspendLayout(); + // + // splitter + // + this.splitter.BackColor = System.Drawing.SystemColors.Control; + this.splitter.Cursor = System.Windows.Forms.Cursors.SizeWE; + this.splitter.Dock = System.Windows.Forms.DockStyle.Left; + this.splitter.Location = new System.Drawing.Point(0, 0); + this.splitter.Name = "splitter"; + this.splitter.Size = new System.Drawing.Size(4, 541); + this.splitter.TabIndex = 1; + this.splitter.MouseLeave += new System.EventHandler(this.RaiseMouseContainerLeave); + this.splitter.MouseMove += new System.Windows.Forms.MouseEventHandler(this.splitter_MouseMove); + this.splitter.MouseDown += new System.Windows.Forms.MouseEventHandler(this.splitter_MouseDown); + this.splitter.MouseUp += new System.Windows.Forms.MouseEventHandler(this.splitter_MouseUp); + this.splitter.MouseEnter += new System.EventHandler(this.RaiseMouseContainerEnter); + // + // tabs + // + this.tabs.Alignment = System.Windows.Forms.TabAlignment.Right; + this.tabs.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.tabs.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tabs.ItemSize = new System.Drawing.Size(100, 26); + this.tabs.Location = new System.Drawing.Point(0, 0); + this.tabs.Margin = new System.Windows.Forms.Padding(0); + this.tabs.Multiline = true; + this.tabs.Name = "tabs"; + this.tabs.Padding = new System.Drawing.Point(10, 5); + this.tabs.SelectedIndex = 0; + this.tabs.Size = new System.Drawing.Size(308, 541); + this.tabs.TabIndex = 0; + this.tabs.TabStop = false; + this.tabs.MouseLeave += new System.EventHandler(this.RaiseMouseContainerLeave); + this.tabs.Selected += new System.Windows.Forms.TabControlEventHandler(this.tabs_Selected); + this.tabs.Enter += new System.EventHandler(this.tabs_Enter); + this.tabs.MouseUp += new System.Windows.Forms.MouseEventHandler(this.tabs_MouseUp); + this.tabs.SelectedIndexChanged += new System.EventHandler(this.tabs_SelectedIndexChanged); + this.tabs.MouseEnter += new System.EventHandler(this.RaiseMouseContainerEnter); + // + // DockersControl + // + this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; + this.Controls.Add(this.splitter); + this.Controls.Add(this.tabs); + this.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.Name = "DockersControl"; + this.Size = new System.Drawing.Size(308, 541); + this.MouseLeave += new System.EventHandler(this.RaiseMouseContainerLeave); + this.MouseEnter += new System.EventHandler(this.RaiseMouseContainerEnter); + this.ResumeLayout(false); + + } + + #endregion + + private DockersTabsControl tabs; + private TransparentPanel splitter; + } +} diff --git a/Source/Core/Controls/DockersControl.cs b/Source/Core/Controls/DockersControl.cs new file mode 100644 index 0000000..322143b --- /dev/null +++ b/Source/Core/Controls/DockersControl.cs @@ -0,0 +1,440 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Text; +using System.Windows.Forms; +using Microsoft.Win32; +using System.Diagnostics; +using CodeImp.DoomBuilder.Actions; +using CodeImp.DoomBuilder.Data; +using CodeImp.DoomBuilder.Config; +using CodeImp.DoomBuilder.Rendering; +using SlimDX.Direct3D9; +using System.Drawing.Imaging; +using System.Drawing.Drawing2D; +using CodeImp.DoomBuilder.Map; +using System.Globalization; + +#endregion + +namespace CodeImp.DoomBuilder.Controls +{ + internal partial class DockersControl : UserControl + { + #region ================== Constants + + #endregion + + #region ================== Delegates + + public event EventHandler MouseContainerEnter; + public event EventHandler MouseContainerLeave; + public event EventHandler Collapsed; + public event EventHandler Expanded; + public event EventHandler UserResize; + + #endregion + + #region ================== Variables + + // Behaviour + private bool rightalign; + private bool iscollapsed; + + // Collapsing + private int expandedwidth; // width when expanded + private int expandedtab; // selected tab index when expanded + + // Splitting + private int splitstartoffset; + + // Selection + private string currentselected; + private string previousselected; + private bool controlledselection; + + #endregion + + #region ================== Properties + + public bool IsCollpased { get { return iscollapsed; } } + + // This returns true when the focus is here, but not in some special cases + public bool IsFocused + { + get + { + Control ac = FindActiveControl(); + + // We have focus when we need the keyboard for input + // Otherwise we don't want the focus and the docker may collapse + return (ac is TextBox) || (ac is RichTextBox) || (ac is NumericUpDown) || (ac is ComboBox); + } + } + + #endregion + + #region ================== Constructor + + // Constructor + public DockersControl() + { + InitializeComponent(); + expandedwidth = (int)((float)this.Width * (this.CurrentAutoScaleDimensions.Width / this.AutoScaleDimensions.Width)); + } + + #endregion + + #region ================== Methods + + // This returns the active child control + private Control FindActiveControl() + { + Control c = this.ActiveControl; + + while (c is IContainerControl) + { + IContainerControl cc = (c as IContainerControl); + if (cc.ActiveControl != null) + c = cc.ActiveControl; + else + break; + } + + return c; + } + + // This sets up the controls for left or right alignment + public void Setup(bool right) + { + rightalign = right; + if (rightalign) + { + splitter.Dock = DockStyle.Left; + tabs.Alignment = TabAlignment.Right; + tabs.Location = new Point(0, 0); + tabs.Size = new Size(this.ClientRectangle.Width + 2, this.ClientRectangle.Height); + } + else + { + splitter.Dock = DockStyle.Right; + tabs.Alignment = TabAlignment.Left; + tabs.Location = new Point(-2, 0); + tabs.Size = new Size(this.ClientRectangle.Width + 2, this.ClientRectangle.Height); + } + + tabs.SendToBack(); + } + + // This collapses the docker + public void Collapse() + { + if (iscollapsed) return; + + controlledselection = true; + splitter.Enabled = false; + splitter.BackColor = SystemColors.Control; + splitter.Width = (int)(2.0f * (this.CurrentAutoScaleDimensions.Width / this.AutoScaleDimensions.Width)); + expandedtab = tabs.SelectedIndex; + expandedwidth = this.Width; + tabs.SelectedIndex = -1; + General.MainWindow.LockUpdate(); + if (rightalign) this.Left = this.Right - GetCollapsedWidth(); + this.Width = GetCollapsedWidth(); + General.MainWindow.UnlockUpdate(); + this.Invalidate(true); + controlledselection = false; + + iscollapsed = true; + + if (Collapsed != null) Collapsed(this, EventArgs.Empty); + } + + // This expands the docker + public void Expand() + { + if (!iscollapsed) return; + + controlledselection = true; + splitter.Enabled = true; + splitter.BackColor = Color.Transparent; + splitter.Width = (int)(4.0f * (this.CurrentAutoScaleDimensions.Width / this.AutoScaleDimensions.Width)); + General.MainWindow.LockUpdate(); + if (rightalign) this.Left = this.Right - expandedwidth; + this.Width = expandedwidth; + General.MainWindow.UnlockUpdate(); + tabs.SelectedIndex = expandedtab; + tabs.Invalidate(true); + controlledselection = false; + + iscollapsed = false; + + if (Expanded != null) Expanded(this, EventArgs.Empty); + } + + // This calculates the collapsed width + public int GetCollapsedWidth() + { + Rectangle r; + if (tabs.TabPages.Count > 0) + r = tabs.GetTabRect(0); + else + r = new Rectangle(0, 0, 26, 26); + return r.Width + (int)(1.0f * (this.CurrentAutoScaleDimensions.Width / this.AutoScaleDimensions.Width)); + } + + // This adds a docker + public void Add(Docker d) + { + // Set up page + TabPage page = new TabPage(d.Title); + page.SuspendLayout(); + page.Font = this.Font; + page.Tag = d; + page.UseVisualStyleBackColor = false; + page.Controls.Add(d.Control); + d.Control.Dock = DockStyle.Fill; + tabs.TabPages.Add(page); + page.ResumeLayout(true); + if (iscollapsed) tabs.SelectedIndex = -1; + + // Go for all controls to add events + Queue todo = new Queue(); + todo.Enqueue(d.Control); + while (todo.Count > 0) + { + Control c = todo.Dequeue(); + c.MouseEnter += RaiseMouseContainerEnter; + c.MouseLeave += RaiseMouseContainerLeave; + foreach (Control cc in c.Controls) + todo.Enqueue(cc); + } + } + + // This removes a docker + public bool Remove(Docker d) + { + foreach (TabPage page in tabs.TabPages) + { + if ((page.Tag as Docker) == d) + { + // Go for all controls to remove events + Queue todo = new Queue(); + todo.Enqueue(d.Control); + while (todo.Count > 0) + { + Control c = todo.Dequeue(); + c.MouseEnter -= RaiseMouseContainerEnter; + c.MouseLeave -= RaiseMouseContainerLeave; + foreach (Control cc in c.Controls) + todo.Enqueue(cc); + } + + // Take down that page + if (page == tabs.SelectedTab) SelectPrevious(); + page.Controls.Clear(); + tabs.TabPages.Remove(page); + return true; + } + } + + return false; + } + + // This selects a docker + public bool SelectDocker(Docker d) + { + int index = 0; + foreach (TabPage page in tabs.TabPages) + { + if ((page.Tag as Docker) == d) + { + if (iscollapsed) + { + previousselected = currentselected; + expandedtab = index; + } + else + tabs.SelectedTab = page; + + return true; + } + + index++; + } + + return false; + } + + // This selectes the previous docker + public void SelectPrevious() + { + if (!string.IsNullOrEmpty(previousselected)) + { + int index = 0; + foreach (TabPage page in tabs.TabPages) + { + if ((page.Tag as Docker).FullName == previousselected) + { + if (iscollapsed) + { + previousselected = currentselected; + expandedtab = index; + } + else + tabs.SelectedTab = page; + + break; + } + + index++; + } + } + } + + // This sorts tabs by their full name + public void SortTabs(IEnumerable fullnames) + { + Dictionary pages = new Dictionary(tabs.TabPages.Count); + foreach (TabPage p in tabs.TabPages) pages.Add((p.Tag as Docker).FullName, p); + tabs.TabPages.Clear(); + + // Add tabs in order as in fullnames + foreach (string name in fullnames) + { + if (pages.ContainsKey(name)) + { + tabs.TabPages.Add(pages[name]); + pages.Remove(name); + } + } + + // Add remaining tabs + foreach (KeyValuePair p in pages) + tabs.TabPages.Add(p.Value); + } + + #endregion + + #region ================== Events + + // This raises the MouseContainerEnter event + private void RaiseMouseContainerEnter(object sender, EventArgs e) + { + if (MouseContainerEnter != null) + MouseContainerEnter(sender, e); + } + + // This raises the MouseContainerLeave event + private void RaiseMouseContainerLeave(object sender, EventArgs e) + { + if (MouseContainerLeave != null) + MouseContainerLeave(sender, e); + } + + // We don't want the focus + private void tabs_Enter(object sender, EventArgs e) { General.MainWindow.FocusDisplay(); } + private void tabs_MouseUp(object sender, MouseEventArgs e) { General.MainWindow.FocusDisplay(); } + private void tabs_Selected(object sender, TabControlEventArgs e) { General.MainWindow.FocusDisplay(); } + + // Tab selected + private void tabs_SelectedIndexChanged(object sender, EventArgs e) + { + if (!controlledselection) + { + // Keep track of previous selected tab + previousselected = currentselected; + if (tabs.SelectedTab != null) + { + Docker d = (tabs.SelectedTab.Tag as Docker); + currentselected = d.FullName; + } + else + { + currentselected = null; + } + } + + General.MainWindow.FocusDisplay(); + } + + // Splitting begins + private void splitter_MouseDown(object sender, MouseEventArgs e) + { + if (e.Button == MouseButtons.Left) + { + splitstartoffset = e.X; + splitter.BackColor = SystemColors.Highlight; + } + } + + // Splitting ends + private void splitter_MouseUp(object sender, MouseEventArgs e) + { + splitter.BackColor = Color.Transparent; + tabs.Invalidate(true); + this.Update(); + General.MainWindow.RedrawDisplay(); + General.MainWindow.Update(); + } + + // Splitting dragged + private void splitter_MouseMove(object sender, MouseEventArgs e) + { + if (e.Button == MouseButtons.Left) + { + General.MainWindow.LockUpdate(); + + // Resize the control + int delta = e.X - splitstartoffset; + int collapsedwidth = GetCollapsedWidth(); + if (rightalign) + { + this.Left += delta; + this.Width -= delta; + if (this.Width < collapsedwidth) + { + this.Left -= collapsedwidth - this.Width; + this.Width = collapsedwidth; + } + } + else + { + this.Width += delta; + if (this.Width < collapsedwidth) + this.Width = collapsedwidth; + } + + General.MainWindow.UnlockUpdate(); + this.Update(); + General.MainWindow.RedrawDisplay(); + General.MainWindow.Update(); + + // Raise event + if (UserResize != null) + UserResize(this, EventArgs.Empty); + } + } + + #endregion + } +} diff --git a/Source/Core/Controls/DockersControl.resx b/Source/Core/Controls/DockersControl.resx new file mode 100644 index 0000000..00e842c --- /dev/null +++ b/Source/Core/Controls/DockersControl.resx @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + True + + + True + + + True + + \ No newline at end of file diff --git a/Source/Core/Controls/DockersTabsControl.cs b/Source/Core/Controls/DockersTabsControl.cs new file mode 100644 index 0000000..5b81ab5 --- /dev/null +++ b/Source/Core/Controls/DockersTabsControl.cs @@ -0,0 +1,315 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Text; +using System.Windows.Forms; +using System.Windows.Forms.VisualStyles; +using Microsoft.Win32; +using System.Diagnostics; +using CodeImp.DoomBuilder.Actions; +using CodeImp.DoomBuilder.Data; +using CodeImp.DoomBuilder.Config; +using CodeImp.DoomBuilder.Rendering; +using SlimDX.Direct3D9; +using System.Drawing.Imaging; +using System.Drawing.Drawing2D; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Types; +using System.Drawing.Text; + +#endregion + +namespace CodeImp.DoomBuilder.Controls +{ + internal class DockersTabsControl : TabControl + { + #region ================== Constants + + #endregion + + #region ================== Variables + + private Bitmap tabsimage; + private int highlighttab; + + #endregion + + #region ================== Properties + + #endregion + + #region ================== Constructor + + // Constructor + public DockersTabsControl() + { + if (VisualStyleInformation.IsSupportedByOS && VisualStyleInformation.IsEnabledByUser) + { + // Style settings + this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true); + this.SetStyle(ControlStyles.SupportsTransparentBackColor, false); + this.SetStyle(ControlStyles.UserPaint, true); + this.SetStyle(ControlStyles.AllPaintingInWmPaint, true); + this.SetStyle(ControlStyles.Opaque, true); + this.UpdateStyles(); + } + + highlighttab = -1; + } + + // Disposer + protected override void Dispose(bool disposing) + { + if (tabsimage != null) + { + tabsimage.Dispose(); + tabsimage = null; + } + + base.Dispose(disposing); + } + + #endregion + + #region ================== Methods + + // This redraws the tabs + protected unsafe void RedrawTabs() + { + // Determine length and width in pixels + int tabslength = 0; + for (int i = 0; i < this.TabPages.Count; i++) + { + Rectangle r = this.GetTabRect(i); + tabslength += r.Height; + } + tabslength += 4; + int tabswidth = this.ItemSize.Height + 2; + + // Dispose old image + if (tabsimage != null) + { + tabsimage.Dispose(); + tabsimage = null; + } + + if (VisualStyleInformation.IsSupportedByOS && VisualStyleInformation.IsEnabledByUser) + { + StringFormat drawformat = new StringFormat(); + drawformat.Alignment = StringAlignment.Center; + drawformat.HotkeyPrefix = HotkeyPrefix.None; + drawformat.LineAlignment = StringAlignment.Center; + + // Create images + tabsimage = new Bitmap(tabswidth, tabslength, PixelFormat.Format32bppArgb); + Bitmap drawimage = new Bitmap(tabslength, tabswidth, PixelFormat.Format32bppArgb); + Graphics g = Graphics.FromImage(drawimage); + + // Render the tabs (backwards when right-aligned) + int posoffset = 0; + int selectedposoffset = -1; + int start = (this.Alignment == TabAlignment.Left) ? 0 : (this.TabPages.Count - 1); + int end = (this.Alignment == TabAlignment.Left) ? this.TabPages.Count : -1; + int step = (this.Alignment == TabAlignment.Left) ? 1 : -1; + for (int i = start; i != end; i += step) + { + VisualStyleRenderer renderer; + Rectangle tr = this.GetTabRect(i); + + // Tab selected? + if (i == this.SelectedIndex) + { + // We will draw this later + selectedposoffset = posoffset; + } + else + { + if (i == highlighttab) + renderer = new VisualStyleRenderer(VisualStyleElement.Tab.TabItem.Hot); + else + renderer = new VisualStyleRenderer(VisualStyleElement.Tab.TabItem.Normal); + + // Draw tab + Rectangle r = new Rectangle(posoffset + 2, 2, tr.Height, tr.Width - 2); + renderer.DrawBackground(g, r); + g.DrawString(this.TabPages[i].Text, this.Font, SystemBrushes.ControlText, new RectangleF(r.Location, r.Size), drawformat); + } + + posoffset += tr.Height; + } + + // Render the selected tab, because it is slightly larger and overlapping the others + if (selectedposoffset > -1) + { + VisualStyleRenderer renderer = new VisualStyleRenderer(VisualStyleElement.Tab.TabItem.Pressed); + Rectangle tr = this.GetTabRect(this.SelectedIndex); + Rectangle r = new Rectangle(selectedposoffset, 0, tr.Height + 4, tr.Width); + renderer.DrawBackground(g, r); + g.DrawString(this.TabPages[this.SelectedIndex].Text, this.Font, SystemBrushes.ControlText, new RectangleF(r.X, r.Y, r.Width, r.Height - 2), drawformat); + } + + // Rotate the image and copy to tabsimage + BitmapData drawndata = drawimage.LockBits(new Rectangle(0, 0, drawimage.Size.Width, drawimage.Size.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb); + BitmapData targetdata = tabsimage.LockBits(new Rectangle(0, 0, tabsimage.Size.Width, tabsimage.Size.Height), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb); + int* dd = (int*)drawndata.Scan0.ToPointer(); + int* td = (int*)targetdata.Scan0.ToPointer(); + if (this.Alignment == TabAlignment.Right) + { + for (int y = 0; y < drawndata.Height; y++) + { + for (int x = 0; x < drawndata.Width; x++) + { + td[(drawndata.Width - 1 - x) * targetdata.Width + y] = *dd; + dd++; + } + } + } + else + { + for (int y = 0; y < drawndata.Height; y++) + { + for (int x = 0; x < drawndata.Width; x++) + { + td[x * targetdata.Width + (drawndata.Height - 1 - y)] = *dd; + dd++; + } + } + } + drawimage.UnlockBits(drawndata); + tabsimage.UnlockBits(targetdata); + + // Clean up + g.Dispose(); + drawimage.Dispose(); + } + } + + #endregion + + #region ================== Events + + // Redrawing needed + protected override void OnPaint(PaintEventArgs e) + { + Point p; + + if (VisualStyleInformation.IsSupportedByOS && VisualStyleInformation.IsEnabledByUser) + { + RedrawTabs(); + + e.Graphics.Clear(SystemColors.Control); + + if (this.Alignment == TabAlignment.Left) + { + p = new Point(0, 0); + } + else + { + int left = this.ClientSize.Width - tabsimage.Size.Width; + if (left < 0) left = 0; + p = new Point(left, 0); + } + + e.Graphics.DrawImage(tabsimage, p); + } + else + { + base.OnPaint(e); + } + } + + // Mouse moves + protected override void OnMouseMove(MouseEventArgs e) + { + if (VisualStyleInformation.IsSupportedByOS && VisualStyleInformation.IsEnabledByUser) + { + int foundindex = -1; + Rectangle prect = new Rectangle(e.Location, Size.Empty); + + // Check in which tab the mouse is + for (int i = this.TabPages.Count - 1; i >= 0; i--) + { + Rectangle tabrect = this.GetTabRect(i); + tabrect.Inflate(1, 1); + if (tabrect.IntersectsWith(prect)) + { + foundindex = i; + break; + } + } + + // Redraw? + if (foundindex != highlighttab) + { + highlighttab = foundindex; + this.Invalidate(); + } + } + + base.OnMouseMove(e); + } + + // Mouse leaves + protected override void OnMouseLeave(EventArgs e) + { + if (VisualStyleInformation.IsSupportedByOS && VisualStyleInformation.IsEnabledByUser) + { + // Redraw? + if (highlighttab != -1) + { + highlighttab = -1; + this.Invalidate(); + } + } + + base.OnMouseLeave(e); + } + + // Tabs don't process keys + protected override void OnKeyDown(KeyEventArgs ke) + { + if (this.Parent is DockersControl) + { + // Only absorb the key press when no focused on an input control, otherwise + // the input controls may not receive certain keys such as delete and arrow keys + DockersControl docker = (this.Parent as DockersControl); + if (!docker.IsFocused) + ke.Handled = true; + } + } + + // Tabs don't process keys + protected override void OnKeyUp(KeyEventArgs e) + { + if (this.Parent is DockersControl) + { + // Only absorb the key press when no focused on an input control, otherwise + // the input controls may not receive certain keys such as delete and arrow keys + DockersControl docker = (this.Parent as DockersControl); + if (!docker.IsFocused) + e.Handled = true; + } + } + + #endregion + } +} diff --git a/Source/Core/Controls/FieldsEditorControl.Designer.cs b/Source/Core/Controls/FieldsEditorControl.Designer.cs new file mode 100644 index 0000000..685b8d1 --- /dev/null +++ b/Source/Core/Controls/FieldsEditorControl.Designer.cs @@ -0,0 +1,186 @@ +namespace CodeImp.DoomBuilder.Controls +{ + partial class FieldsEditorControl + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle4 = new System.Windows.Forms.DataGridViewCellStyle(); + System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle1 = new System.Windows.Forms.DataGridViewCellStyle(); + System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle2 = new System.Windows.Forms.DataGridViewCellStyle(); + System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle3 = new System.Windows.Forms.DataGridViewCellStyle(); + this.fieldslist = new System.Windows.Forms.DataGridView(); + this.fieldname = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.fieldtype = new System.Windows.Forms.DataGridViewComboBoxColumn(); + this.fieldvalue = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.deleterowstimer = new System.Windows.Forms.Timer(this.components); + this.browsebutton = new System.Windows.Forms.Button(); + this.enumscombo = new System.Windows.Forms.ComboBox(); + ((System.ComponentModel.ISupportInitialize)(this.fieldslist)).BeginInit(); + this.SuspendLayout(); + // + // fieldslist + // + this.fieldslist.AllowUserToResizeColumns = false; + this.fieldslist.AllowUserToResizeRows = false; + this.fieldslist.BackgroundColor = System.Drawing.SystemColors.Window; + this.fieldslist.BorderStyle = System.Windows.Forms.BorderStyle.None; + this.fieldslist.CellBorderStyle = System.Windows.Forms.DataGridViewCellBorderStyle.None; + this.fieldslist.ClipboardCopyMode = System.Windows.Forms.DataGridViewClipboardCopyMode.Disable; + this.fieldslist.ColumnHeadersBorderStyle = System.Windows.Forms.DataGridViewHeaderBorderStyle.Single; + this.fieldslist.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize; + this.fieldslist.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] { + this.fieldname, + this.fieldtype, + this.fieldvalue}); + dataGridViewCellStyle4.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft; + dataGridViewCellStyle4.BackColor = System.Drawing.SystemColors.Window; + dataGridViewCellStyle4.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + dataGridViewCellStyle4.ForeColor = System.Drawing.SystemColors.WindowText; + dataGridViewCellStyle4.SelectionBackColor = System.Drawing.SystemColors.Highlight; + dataGridViewCellStyle4.SelectionForeColor = System.Drawing.SystemColors.HighlightText; + dataGridViewCellStyle4.WrapMode = System.Windows.Forms.DataGridViewTriState.False; + this.fieldslist.DefaultCellStyle = dataGridViewCellStyle4; + this.fieldslist.EditMode = System.Windows.Forms.DataGridViewEditMode.EditProgrammatically; + this.fieldslist.Location = new System.Drawing.Point(0, 0); + this.fieldslist.MultiSelect = false; + this.fieldslist.Name = "fieldslist"; + this.fieldslist.RowHeadersVisible = false; + this.fieldslist.RowTemplate.DefaultCellStyle.BackColor = System.Drawing.SystemColors.Window; + this.fieldslist.RowTemplate.DefaultCellStyle.ForeColor = System.Drawing.SystemColors.WindowText; + this.fieldslist.RowTemplate.DefaultCellStyle.SelectionBackColor = System.Drawing.SystemColors.Highlight; + this.fieldslist.RowTemplate.DefaultCellStyle.SelectionForeColor = System.Drawing.SystemColors.HighlightText; + this.fieldslist.ScrollBars = System.Windows.Forms.ScrollBars.Vertical; + this.fieldslist.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.FullRowSelect; + this.fieldslist.Size = new System.Drawing.Size(444, 263); + this.fieldslist.TabIndex = 1; + this.fieldslist.Scroll += new System.Windows.Forms.ScrollEventHandler(this.fieldslist_Scroll); + this.fieldslist.UserDeletingRow += new System.Windows.Forms.DataGridViewRowCancelEventHandler(this.fieldslist_UserDeletingRow); + this.fieldslist.CellBeginEdit += new System.Windows.Forms.DataGridViewCellCancelEventHandler(this.fieldslist_CellBeginEdit); + this.fieldslist.CellDoubleClick += new System.Windows.Forms.DataGridViewCellEventHandler(this.fieldslist_CellDoubleClick); + this.fieldslist.ColumnHeaderMouseClick += new System.Windows.Forms.DataGridViewCellMouseEventHandler(this.fieldslist_ColumnHeaderMouseClick); + this.fieldslist.MouseUp += new System.Windows.Forms.MouseEventHandler(this.fieldslist_MouseUp); + this.fieldslist.CellEndEdit += new System.Windows.Forms.DataGridViewCellEventHandler(this.fieldslist_CellEndEdit); + this.fieldslist.CellClick += new System.Windows.Forms.DataGridViewCellEventHandler(this.fieldslist_CellClick); + this.fieldslist.DataError += new System.Windows.Forms.DataGridViewDataErrorEventHandler(this.fieldslist_DataError); + this.fieldslist.SelectionChanged += new System.EventHandler(this.fieldslist_SelectionChanged); + // + // fieldname + // + dataGridViewCellStyle1.BackColor = System.Drawing.SystemColors.Window; + dataGridViewCellStyle1.ForeColor = System.Drawing.SystemColors.WindowText; + dataGridViewCellStyle1.NullValue = null; + dataGridViewCellStyle1.SelectionBackColor = System.Drawing.SystemColors.Highlight; + dataGridViewCellStyle1.SelectionForeColor = System.Drawing.SystemColors.HighlightText; + this.fieldname.DefaultCellStyle = dataGridViewCellStyle1; + this.fieldname.Frozen = true; + this.fieldname.HeaderText = "Property"; + this.fieldname.Name = "fieldname"; + this.fieldname.Width = 150; + // + // fieldtype + // + this.fieldtype.AutoComplete = false; + dataGridViewCellStyle2.BackColor = System.Drawing.SystemColors.Window; + dataGridViewCellStyle2.ForeColor = System.Drawing.SystemColors.WindowText; + dataGridViewCellStyle2.SelectionBackColor = System.Drawing.SystemColors.Highlight; + dataGridViewCellStyle2.SelectionForeColor = System.Drawing.SystemColors.HighlightText; + this.fieldtype.DefaultCellStyle = dataGridViewCellStyle2; + this.fieldtype.DisplayStyle = System.Windows.Forms.DataGridViewComboBoxDisplayStyle.Nothing; + this.fieldtype.HeaderText = "Type"; + this.fieldtype.Name = "fieldtype"; + this.fieldtype.Resizable = System.Windows.Forms.DataGridViewTriState.True; + this.fieldtype.Sorted = true; + this.fieldtype.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.Automatic; + // + // fieldvalue + // + dataGridViewCellStyle3.BackColor = System.Drawing.SystemColors.Window; + dataGridViewCellStyle3.ForeColor = System.Drawing.SystemColors.WindowText; + dataGridViewCellStyle3.SelectionBackColor = System.Drawing.SystemColors.Highlight; + dataGridViewCellStyle3.SelectionForeColor = System.Drawing.SystemColors.HighlightText; + this.fieldvalue.DefaultCellStyle = dataGridViewCellStyle3; + this.fieldvalue.HeaderText = "Value"; + this.fieldvalue.Name = "fieldvalue"; + this.fieldvalue.Resizable = System.Windows.Forms.DataGridViewTriState.True; + this.fieldvalue.Width = 120; + // + // deleterowstimer + // + this.deleterowstimer.Interval = 1; + this.deleterowstimer.Tick += new System.EventHandler(this.deleterowstimer_Tick); + // + // browsebutton + // + this.browsebutton.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.browsebutton.Image = global::CodeImp.DoomBuilder.Properties.Resources.treeview; + this.browsebutton.ImageAlign = System.Drawing.ContentAlignment.BottomCenter; + this.browsebutton.Location = new System.Drawing.Point(343, 75); + this.browsebutton.Name = "browsebutton"; + this.browsebutton.Size = new System.Drawing.Size(28, 26); + this.browsebutton.TabIndex = 2; + this.browsebutton.UseVisualStyleBackColor = true; + this.browsebutton.Click += new System.EventHandler(this.browsebutton_Click); + // + // enumscombo + // + this.enumscombo.FormattingEnabled = true; + this.enumscombo.ItemHeight = 14; + this.enumscombo.Location = new System.Drawing.Point(295, 121); + this.enumscombo.Name = "enumscombo"; + this.enumscombo.Size = new System.Drawing.Size(128, 22); + this.enumscombo.TabIndex = 3; + this.enumscombo.Validating += new System.ComponentModel.CancelEventHandler(this.enumscombo_Validating); + // + // FieldsEditorControl + // + this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; + this.Controls.Add(this.enumscombo); + this.Controls.Add(this.browsebutton); + this.Controls.Add(this.fieldslist); + this.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.Name = "FieldsEditorControl"; + this.Size = new System.Drawing.Size(474, 286); + this.Layout += new System.Windows.Forms.LayoutEventHandler(this.FieldsEditorControl_Layout); + this.Resize += new System.EventHandler(this.FieldsEditorControl_Resize); + ((System.ComponentModel.ISupportInitialize)(this.fieldslist)).EndInit(); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.DataGridView fieldslist; + private System.Windows.Forms.Timer deleterowstimer; + private System.Windows.Forms.Button browsebutton; + private System.Windows.Forms.ComboBox enumscombo; + private System.Windows.Forms.DataGridViewTextBoxColumn fieldname; + private System.Windows.Forms.DataGridViewComboBoxColumn fieldtype; + private System.Windows.Forms.DataGridViewTextBoxColumn fieldvalue; + } +} diff --git a/Source/Core/Controls/FieldsEditorControl.cs b/Source/Core/Controls/FieldsEditorControl.cs new file mode 100644 index 0000000..86e6792 --- /dev/null +++ b/Source/Core/Controls/FieldsEditorControl.cs @@ -0,0 +1,751 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Text; +using System.Windows.Forms; +using Microsoft.Win32; +using System.Diagnostics; +using CodeImp.DoomBuilder.Actions; +using CodeImp.DoomBuilder.Data; +using CodeImp.DoomBuilder.Config; +using CodeImp.DoomBuilder.Rendering; +using SlimDX.Direct3D9; +using System.Drawing.Imaging; +using System.Drawing.Drawing2D; +using CodeImp.DoomBuilder.Map; +using System.Globalization; + +#endregion + +namespace CodeImp.DoomBuilder.Controls +{ + /// + /// Control to view and/or change custom UDMF fields. + /// + public partial class FieldsEditorControl : UserControl + { + #region ================== Constants + + // Constants + private const string ADD_FIELD_TEXT = " (click to add custom field)"; + private const string FIELD_PREFIX_SUGGESTION = "user_"; + + #endregion + + #region ================== Variables + + // Variables + private string elementname; + private string lasteditfieldname; + + #endregion + + #region ================== Constructor + + // Constructor + public FieldsEditorControl() + { + InitializeComponent(); + enumscombo.Location = new Point(-1000, 1); + } + + #endregion + + #region ================== Setup / Apply + + // This sets up the control + public void Setup(string elementname) + { + // Keep element name + this.elementname = elementname; + + // Make types list + fieldtype.Items.Clear(); + fieldtype.Items.AddRange(General.Types.GetCustomUseAttributes()); + } + + // This applies last sort order + private void Sort() + { + // Sort + int sortcolumn = General.Settings.ReadSetting("customfieldssortcolumn", 0); + int sortorder = General.Settings.ReadSetting("customfieldssortorder", (int)ListSortDirection.Ascending); + + if (sortorder == (int)SortOrder.Ascending) + fieldslist.Sort(fieldslist.Columns[sortcolumn], ListSortDirection.Ascending); + else if (sortorder == (int)SortOrder.Descending) + fieldslist.Sort(fieldslist.Columns[sortcolumn], ListSortDirection.Descending); + } + + // This adds a list of fixed fields (in undefined state) + public void ListFixedFields(List list) + { + // Add all fields + foreach (UniversalFieldInfo uf in list) + fieldslist.Rows.Add(new FieldsEditorRow(fieldslist, uf)); + + // Sort fields + Sort(); + + // Update new row + SetupNewRowStyle(); + } + + // This sets up the fields and values from a UniFields object + // When first is true, the values are applied unconditionally + // When first is false, the values in the grid are erased when + // they differ from the given values (for multiselection) + public void SetValues(UniFields fromfields, bool first) + { + // Go for all the fields + foreach (KeyValuePair f in fromfields) + { + // Go for all rows + bool foundrow = false; + foreach (DataGridViewRow row in fieldslist.Rows) + { + // Row is a field? + if (row is FieldsEditorRow) + { + FieldsEditorRow frow = row as FieldsEditorRow; + + // Row name matches with field + if (frow.Name == f.Key) + { + // First time? + if (first) + { + // Set type when row is not fixed + if (!frow.IsFixed) frow.ChangeType(f.Value.Type); + + // Apply value of field to row + frow.Define(f.Value.Value); + } + else + { + // Check if the value is different + if (!frow.TypeHandler.GetValue().Equals(f.Value.Value)) + { + // Clear the value in the row + frow.Define(f.Value.Value); + frow.Clear(); + } + } + + // Done + foundrow = true; + break; + } + } + } + + // Row not found? + if (!foundrow) + { + // Make new row + FieldsEditorRow frow = new FieldsEditorRow(fieldslist, f.Key, f.Value.Type, f.Value.Value); + fieldslist.Rows.Insert(fieldslist.Rows.Count - 1, frow); + + // When not the first, clear the field + // because the others did not define this one + if (!first) frow.Clear(); + } + } + + // Now check for rows that the givens fields do NOT have + foreach (DataGridViewRow row in fieldslist.Rows) + { + // Row is a field? + if (row is FieldsEditorRow) + { + FieldsEditorRow frow = row as FieldsEditorRow; + + // Is this row defined previously? + if (frow.IsDefined) + { + // Check if this row can not be found in the fields at all + if (!fromfields.ContainsKey(frow.Name)) + { + // It is not defined in these fields, clear the value + frow.Clear(); + } + } + } + } + + // Sort fields + Sort(); + } + + // This applies the current fields to a UniFields object + public void Apply(UniFields tofields) + { + tofields.BeforeFieldsChange(); + + // Go for all the fields + UniFields tempfields = new UniFields(tofields); + foreach (KeyValuePair f in tempfields) + { + // Go for all rows + bool foundrow = false; + foreach (DataGridViewRow row in fieldslist.Rows) + { + // Row is a field and matches field name? + if ((row is FieldsEditorRow) && (row.Cells[0].Value.ToString() == f.Key)) + { + FieldsEditorRow frow = row as FieldsEditorRow; + + // Field is defined? + if (frow.IsDefined) + { + foundrow = true; + break; + } + } + } + + // No such row? + if (!foundrow) + { + // Remove the definition from the fields + tofields.Remove(f.Key); + } + } + + // Go for all rows + foreach (DataGridViewRow row in fieldslist.Rows) + { + // Row is a field? + if (row is FieldsEditorRow) + { + FieldsEditorRow frow = row as FieldsEditorRow; + + // Field is defined and not empty? + if (frow.IsDefined && !frow.IsEmpty) + { + // Apply field + object oldvalue = null; + if (tofields.ContainsKey(frow.Name)) oldvalue = tofields[frow.Name].Value; + tofields[frow.Name] = new UniValue(frow.TypeHandler.Index, frow.GetResult(oldvalue)); + + // Custom row? + if (!frow.IsFixed) + { + // Write type to map configuration + General.Map.Options.SetUniversalFieldType(elementname, frow.Name, frow.TypeHandler.Index); + } + } + } + } + } + + #endregion + + #region ================== Events + + // Column header clicked + private void fieldslist_ColumnHeaderMouseClick(object sender, DataGridViewCellMouseEventArgs e) + { + // Save sort order + if (fieldslist.SortedColumn != null) + { + int sortcolumn = fieldslist.SortedColumn.Index; + int sortorder = (int)fieldslist.SortOrder; + General.Settings.WriteSetting("customfieldssortcolumn", sortcolumn); + General.Settings.WriteSetting("customfieldssortorder", sortorder); + } + + // Stop any cell editing + ApplyEnums(true); + fieldslist.EndEdit(); + HideBrowseButton(); + } + + // Resized + private void FieldsEditorControl_Resize(object sender, EventArgs e) + { + // Rearrange controls + fieldslist.Size = this.ClientSize; + fieldvalue.Width = fieldslist.ClientRectangle.Width - fieldname.Width - fieldtype.Width - SystemInformation.VerticalScrollBarWidth - 10; + UpdateBrowseButton(); + } + + // Layout change + private void FieldsEditorControl_Layout(object sender, LayoutEventArgs e) + { + FieldsEditorControl_Resize(sender, EventArgs.Empty); + } + + // Cell clicked + private void fieldslist_CellClick(object sender, DataGridViewCellEventArgs e) + { + ApplyEnums(true); + + // Anything selected + if (fieldslist.SelectedCells.Count > 0) + { + if (e.RowIndex == fieldslist.NewRowIndex) + fieldslist.BeginEdit(false); + else if (e.ColumnIndex > 0) + fieldslist.BeginEdit(true); + } + } + + // Cell doubleclicked + private void fieldslist_CellDoubleClick(object sender, DataGridViewCellEventArgs e) + { + FieldsEditorRow frow = null; + + // Anything selected + if (fieldslist.SelectedRows.Count > 0) + { + // Get the row + DataGridViewRow row = fieldslist.Rows[e.RowIndex]; + if (row is FieldsEditorRow) frow = row as FieldsEditorRow; + + // First column? + if (e.ColumnIndex == 0) + { + // Not a fixed field? + if ((frow != null) && !frow.IsFixed) + { + lasteditfieldname = frow.Name; + fieldslist.CurrentCell = fieldslist.SelectedRows[0].Cells[0]; + fieldslist.CurrentCell.ReadOnly = false; + + if ((e.RowIndex == fieldslist.NewRowIndex) || + frow.Name.StartsWith(FIELD_PREFIX_SUGGESTION, true, CultureInfo.InvariantCulture)) + fieldslist.BeginEdit(false); + else + fieldslist.BeginEdit(true); + } + } + } + } + + // User deletes a row + private void fieldslist_UserDeletingRow(object sender, DataGridViewRowCancelEventArgs e) + { + // Get the row + FieldsEditorRow row = e.Row as FieldsEditorRow; + + // Fixed field? + if (row.IsFixed) + { + // Just undefine the field + row.Undefine(); + e.Cancel = true; + } + } + + // User selects a cell for editing + private void fieldslist_CellBeginEdit(object sender, DataGridViewCellCancelEventArgs e) + { + // Field name cell? + if (e.ColumnIndex == 0) + { + // New row index? + if (e.RowIndex == fieldslist.NewRowIndex) + { + // Remove all text + fieldslist.Rows[e.RowIndex].Cells[0].Style.ForeColor = SystemColors.WindowText; + fieldslist.Rows[e.RowIndex].Cells[0].Value = FIELD_PREFIX_SUGGESTION; + } + } + // Value cell? + else if (e.ColumnIndex == 2) + { + // Get the row + FieldsEditorRow frow = null; + DataGridViewRow row = fieldslist.Rows[e.RowIndex]; + if (row is FieldsEditorRow) + { + // Get specializedrow + frow = row as FieldsEditorRow; + + // Enumerable? + if (frow.TypeHandler.IsEnumerable) + { + // Fill combo with enums + enumscombo.SelectedItem = null; + enumscombo.Text = ""; + enumscombo.Items.Clear(); + enumscombo.Items.AddRange(frow.TypeHandler.GetEnumList().ToArray()); + enumscombo.Tag = frow; + + // Lock combo to enums? + if (frow.TypeHandler.IsLimitedToEnums) + enumscombo.DropDownStyle = ComboBoxStyle.DropDownList; + else + enumscombo.DropDownStyle = ComboBoxStyle.DropDown; + + // Position combobox + Rectangle cellrect = fieldslist.GetCellDisplayRectangle(2, row.Index, false); + enumscombo.Location = new Point(cellrect.Left, cellrect.Top); + enumscombo.Width = cellrect.Width; + int internalheight = cellrect.Height - (enumscombo.Height - enumscombo.ClientRectangle.Height) - 6; + General.SendMessage(enumscombo.Handle, General.CB_SETITEMHEIGHT, -1, internalheight); + + // Select the value of this field (for DropDownList style combo) + foreach (EnumItem i in enumscombo.Items) + { + // Matches? + if (string.Compare(i.Title, frow.TypeHandler.GetStringValue(), true, CultureInfo.InvariantCulture) == 0) + { + // Select this item + enumscombo.SelectedItem = i; + } + } + + // Put the display text in the text (for DropDown style combo) + enumscombo.Text = frow.TypeHandler.GetStringValue(); + + // Show combo + enumscombo.Show(); + } + } + } + } + + // Done editing cell contents + private void fieldslist_CellEndEdit(object sender, DataGridViewCellEventArgs e) + { + FieldsEditorRow frow = null; + DataGridViewRow row = null; + + // Get the row + row = fieldslist.Rows[e.RowIndex]; + if (row is FieldsEditorRow) frow = row as FieldsEditorRow; + + // Renaming a field? + if (e.ColumnIndex == 0) + { + // Row is a new row? + if (frow == null) + { + // Name given? + if ((row.Cells[0].Value != null) && (row.Cells[0].Value.ToString() != FIELD_PREFIX_SUGGESTION)) + { + // Make a valid UDMF field name + string validname = UniValue.ValidateName(row.Cells[0].Value.ToString()); + if (validname.Length > 0) + { + // Check if no other row already has this name + foreach (DataGridViewRow r in fieldslist.Rows) + { + // Name matches and not the same row? + if ((r.Index != row.Index) && (r.Cells.Count > 0) && (r.Cells[0].Value != null) && + (r.Cells[0].Value.ToString().ToLowerInvariant() == validname)) + { + // Cannot have two rows with same name + validname = ""; + General.ShowWarningMessage("Fields must have unique names!", MessageBoxButtons.OK); + break; + } + } + + // Still valid? + if (validname.Length > 0) + { + // Try to find the type in the map options + int type = General.Map.Options.GetUniversalFieldType(elementname, validname, 0); + + // Make new row + frow = new FieldsEditorRow(fieldslist, validname, type, null); + frow.Visible = false; + fieldslist.Rows.Insert(e.RowIndex + 1, frow); + } + } + } + + // Mark the row for delete + row.ReadOnly = true; + deleterowstimer.Start(); + } + else + { + // Name given? + if (row.Cells[0].Value != null) + { + // Make a valid UDMF field name + string validname = UniValue.ValidateName(row.Cells[0].Value.ToString()); + if (validname.Length > 0) + { + // Check if no other row already has this name + foreach (DataGridViewRow r in fieldslist.Rows) + { + // Name matches and not the same row? + if ((r.Index != row.Index) && (r.Cells.Count > 0) && (r.Cells[0].Value != null) && + (r.Cells[0].Value.ToString().ToLowerInvariant() == validname)) + { + // Cannot have two rows with same name + validname = ""; + row.Cells[0].Value = lasteditfieldname; + General.ShowWarningMessage("Fields must have unique names!", MessageBoxButtons.OK); + break; + } + } + + // Still valid? + if (validname.Length > 0) + { + // Try to find the type in the map options + int type = General.Map.Options.GetUniversalFieldType(elementname, validname, -1); + + // Rename row and change type + row.Cells[0].Value = validname; + if (type != -1) frow.ChangeType(type); + } + else + { + // Keep old name + row.Cells[0].Value = lasteditfieldname; + } + } + else + { + // Keep old name + row.Cells[0].Value = lasteditfieldname; + } + } + else + { + // Keep old name + row.Cells[0].Value = lasteditfieldname; + } + } + } + // Changing field value? + if ((e.ColumnIndex == 2) && (frow != null)) + { + // Apply changes + ApplyValue(frow, row.Cells[2].Value); + } + + // Updated + if (frow != null) frow.CellChanged(); + + // Update button + UpdateBrowseButton(); + } + + // Time to delete rows + private void deleterowstimer_Tick(object sender, EventArgs e) + { + // Stop timer + deleterowstimer.Stop(); + + // Delete all rows that must be deleted + for (int i = fieldslist.Rows.Count - 1; i >= 0; i--) + { + if (fieldslist.Rows[i].ReadOnly) + try { fieldslist.Rows.RemoveAt(i); } catch (Exception) { } + else + fieldslist.Rows[i].Visible = true; + } + + // Update new row + SetupNewRowStyle(); + + // Update button + UpdateBrowseButton(); + } + + // Selection changes + private void fieldslist_SelectionChanged(object sender, EventArgs e) + { + browsebutton.Visible = false; + ApplyEnums(true); + + // Update button + UpdateBrowseButton(); + } + + // Browse clicked + private void browsebutton_Click(object sender, EventArgs e) + { + // Any row selected? + if (fieldslist.SelectedRows.Count > 0) + { + // Get selected row + DataGridViewRow row = fieldslist.SelectedRows[0]; + if (row is FieldsEditorRow) + { + // Browse + (row as FieldsEditorRow).Browse(this.ParentForm); + } + } + } + + // This handles field data errors + private void fieldslist_DataError(object sender, DataGridViewDataErrorEventArgs e) + { + // Ignore this, because we want to display values + // in the type column that are not in their combobox + e.ThrowException = false; + } + + // Validate value in enums combobox + private void enumscombo_Validating(object sender, CancelEventArgs e) + { + ApplyEnums(false); + } + + // Scrolling + private void fieldslist_Scroll(object sender, ScrollEventArgs e) + { + // Stop any cell editing + ApplyEnums(true); + fieldslist.EndEdit(); + HideBrowseButton(); + } + + // Mouse up event + private void fieldslist_MouseUp(object sender, MouseEventArgs e) + { + // Focus to enums combobox when visible + if (enumscombo.Visible) + { + enumscombo.Focus(); + enumscombo.SelectAll(); + } + } + + #endregion + + #region ================== Private Methods + + // This applies a value to a row + private void ApplyValue(FieldsEditorRow frow, object value) + { + // Defined? + if ((value != null) && (!frow.IsFixed || !frow.Info.Default.Equals(value))) + frow.Define(value); + else if (frow.IsFixed) + frow.Undefine(); + } + + // This applies the contents of the enums combobox and hides (if opened) + private void ApplyEnums(bool hide) + { + // Enums combobox shown? + if ((enumscombo.Visible) && (enumscombo.Tag is FieldsEditorRow)) + { + // Get the row + FieldsEditorRow frow = (enumscombo.Tag as FieldsEditorRow); + + // Take the selected value and apply it + ApplyValue(frow, enumscombo.Text); + + // Updated + frow.CellChanged(); + } + + if (hide) + { + // Hide combobox + enumscombo.Tag = null; + enumscombo.Visible = false; + enumscombo.Items.Clear(); + } + } + + // This sets up the new row + private void SetupNewRowStyle() + { + // Show text for new row + fieldslist.Rows[fieldslist.NewRowIndex].Cells[0].Value = ADD_FIELD_TEXT; + fieldslist.Rows[fieldslist.NewRowIndex].Cells[0].Style.ForeColor = SystemColors.GrayText; + fieldslist.Rows[fieldslist.NewRowIndex].Cells[0].ReadOnly = false; + + // Make sure user can only enter property name in a new row + fieldslist.Rows[fieldslist.NewRowIndex].Cells[1].ReadOnly = true; + fieldslist.Rows[fieldslist.NewRowIndex].Cells[2].ReadOnly = true; + } + + // This hides the browse button + private void HideBrowseButton() + { + browsebutton.Visible = false; + } + + // This updates the button + private void UpdateBrowseButton() + { + FieldsEditorRow frow = null; + DataGridViewRow row = null; + + // Any row selected? + if (fieldslist.SelectedRows.Count > 0) + { + // Get selected row + row = fieldslist.SelectedRows[0]; + if (row is FieldsEditorRow) frow = row as FieldsEditorRow; + + // Not the new row and FieldsEditorRow available? + if ((row.Index < fieldslist.NewRowIndex) && (frow != null)) + { + // Browse button available for this type? + if (frow.TypeHandler.IsBrowseable && !frow.TypeHandler.IsEnumerable) + { + Rectangle cellrect = fieldslist.GetCellDisplayRectangle(2, row.Index, false); + + // Show button + enumscombo.Visible = false; + browsebutton.Location = new Point(cellrect.Right - browsebutton.Width, cellrect.Top); + browsebutton.Height = cellrect.Height; + browsebutton.Visible = true; + + /* + // Determine image/text + if((frow.Type == UniversalFieldType.SectorEffect) || + (frow.Type == UniversalFieldType.LinedefAction)) + { + browsebutton.Image = CodeImp.DoomBuilder.Properties.Resources.treeview; + browsebutton.Text = ""; + } + else + { + browsebutton.Image = null; + browsebutton.Text = "..."; + } + */ + } + else + { + HideBrowseButton(); + } + } + else + { + HideBrowseButton(); + } + } + else + { + HideBrowseButton(); + } + } + + #endregion + } +} diff --git a/Source/Core/Controls/FieldsEditorControl.resx b/Source/Core/Controls/FieldsEditorControl.resx new file mode 100644 index 0000000..fca4bd4 --- /dev/null +++ b/Source/Core/Controls/FieldsEditorControl.resx @@ -0,0 +1,144 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + True + + + True + + + True + + + True + + + 17, 17 + + + True + + + True + + + True + + \ No newline at end of file diff --git a/Source/Core/Controls/FieldsEditorRow.cs b/Source/Core/Controls/FieldsEditorRow.cs new file mode 100644 index 0000000..3ae9f78 --- /dev/null +++ b/Source/Core/Controls/FieldsEditorRow.cs @@ -0,0 +1,281 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Text; +using System.Windows.Forms; +using Microsoft.Win32; +using System.Diagnostics; +using CodeImp.DoomBuilder.Actions; +using CodeImp.DoomBuilder.Data; +using CodeImp.DoomBuilder.Config; +using CodeImp.DoomBuilder.Rendering; +using SlimDX.Direct3D9; +using System.Drawing.Imaging; +using System.Drawing.Drawing2D; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Types; + +#endregion + +namespace CodeImp.DoomBuilder.Controls +{ + internal class FieldsEditorRow : DataGridViewRow + { + #region ================== Constants + + #endregion + + #region ================== Variables + + // This is true when for a fixed field as defined in the game configuration + // This means that the field cannot be deleted (delete will result in a reset) + // and cannot change type. + private bool isfixed; + + // Field information (only for fixed fields) + private UniversalFieldInfo fieldinfo; + + // This is true when the field is defined. Cannot be false when this field + // is not fixed, because non-fixed fields are deleted from the list when undefined. + private bool isdefined; + + // Type + private TypeHandler fieldtype; + + #endregion + + #region ================== Properties + + public bool IsFixed { get { return isfixed; } } + public bool IsDefined { get { return isdefined; } } + public bool IsEmpty { get { return (this.Cells[2].Value == null) || (this.Cells[2].Value.ToString().Length == 0); } } + public string Name { get { return this.Cells[0].Value.ToString(); } } + public TypeHandler TypeHandler { get { return fieldtype; } } + public UniversalFieldInfo Info { get { return fieldinfo; } } + + #endregion + + #region ================== Constructor + + // Constructor for a fixed, undefined field + public FieldsEditorRow(DataGridView view, UniversalFieldInfo fixedfield) + { + // Undefined + this.DefaultCellStyle.ForeColor = SystemColors.GrayText; + isdefined = false; + + // Fixed + this.fieldinfo = fixedfield; + isfixed = true; + + // Type + this.fieldtype = General.Types.GetFieldHandler(fixedfield); + + // Make all cells + base.CreateCells(view); + + // Setup property cell + this.Cells[0].Value = fixedfield.Name; + this.Cells[0].ReadOnly = true; + + // Setup type cell + this.Cells[1].Value = fieldtype.GetDisplayType(); + this.Cells[1].ReadOnly = true; + + // Setup value cell + this.Cells[2].Value = fieldtype.GetStringValue(); + + // We have no destructor + GC.SuppressFinalize(this); + } + + // Constructor for a non-fixed, defined field + public FieldsEditorRow(DataGridView view, string name, int type, object value) + { + // Defined + this.DefaultCellStyle.ForeColor = SystemColors.WindowText; + isdefined = true; + + // Non-fixed + isfixed = false; + + // Type + this.fieldtype = General.Types.GetFieldHandler(type, value); + + // Make all cells + base.CreateCells(view); + + // Setup property cell + this.Cells[0].Value = name; + this.Cells[0].ReadOnly = true; + + // Setup type cell + this.Cells[1].Value = fieldtype.GetDisplayType(); + this.Cells[1].ReadOnly = false; + + // Setup value cell + this.Cells[2].Value = fieldtype.GetStringValue(); + + // We have no destructor + GC.SuppressFinalize(this); + } + + #endregion + + #region ================== Methods + + // Browse for value + public void Browse(IWin32Window parent) + { + if (fieldtype != null) + { + // Browse for value + fieldtype.Browse(parent); + + // This is a fixed field? + if (isfixed) + { + // Does this match the default setting? + if (fieldtype.GetValue().Equals(fieldinfo.Default)) + { + // Undefine this field! + Undefine(); + } + else + { + // Define + Define(fieldtype.GetValue()); + } + } + else + { + // Define + Define(fieldtype.GetValue()); + } + } + } + + // This is called when a cell is edited + public void CellChanged() + { + // This gdmn grid thing returns the chosen value as string instead + // of the object type I added to the combobox... + if (this.Cells[1].Value is string) + { + // Find the TypeHandlerAttribute with this name + TypeHandlerAttribute attrib = General.Types.GetNamedAttribute(this.Cells[1].Value.ToString()); + + // Different? + if (attrib.Index != fieldtype.Index) + { + // Change field type! + this.ChangeType(attrib.Index); + } + } + + // Anything in the box? + if ((this.Cells[2].Value != null) && (this.Cells[2].Value.ToString().Length > 0)) + { + // Validate value + fieldtype.SetValue(this.Cells[2].Value); + this.Cells[2].Value = fieldtype.GetStringValue(); + + // This is a fixed field? + if (isfixed) + { + // Does this match the default setting? + if (fieldtype.GetValue().Equals(fieldinfo.Default)) + { + // Undefine this field! + Undefine(); + } + } + } + } + + // This undefines the field + // ONLY VALID FOR FIXED FIELDS + // You should just delete non-fixed fields + public void Undefine() + { + // Must be fixed! + if (!isfixed) throw new InvalidOperationException(); + + // Now undefined + fieldtype.SetValue(fieldinfo.Default); + this.Cells[2].Value = fieldtype.GetStringValue(); + this.DefaultCellStyle.ForeColor = SystemColors.GrayText; + isdefined = false; + } + + // This defines the field + public void Define(object value) + { + // Now defined + fieldtype.SetValue(value); + this.Cells[2].Value = fieldtype.GetStringValue(); + this.DefaultCellStyle.ForeColor = SystemColors.WindowText; + isdefined = true; + } + + // This changes the type + public void ChangeType(int typeindex) + { + // Can't do this for a fixed field! + if (isfixed) throw new InvalidOperationException(); + + // Different? + if (typeindex != fieldtype.Index) + { + // Change field type! + TypeHandlerAttribute attrib = General.Types.GetAttribute(typeindex); + fieldtype = General.Types.GetFieldHandler(typeindex, this.Cells[2].Value); + this.Cells[1].Value = fieldtype.GetDisplayType(); + } + } + + // This clears the field + public void Clear() + { + this.Cells[2].Value = ""; + } + + // This returns the result + public object GetResult(object value) + { + // Anything in the box? + if ((this.Cells[2].Value != null) && (this.Cells[2].Value.ToString().Length > 0)) + { + // Return validated value + fieldtype.SetValue(this.Cells[2].Value); + return fieldtype.GetValue(); + } + else + { + // Return old value + return value; + } + } + + #endregion + } +} diff --git a/Source/Core/Controls/FlatSelectorControl.cs b/Source/Core/Controls/FlatSelectorControl.cs new file mode 100644 index 0000000..97aa237 --- /dev/null +++ b/Source/Core/Controls/FlatSelectorControl.cs @@ -0,0 +1,78 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Text; +using System.Windows.Forms; +using Microsoft.Win32; +using System.Diagnostics; +using CodeImp.DoomBuilder.Actions; +using CodeImp.DoomBuilder.Data; +using CodeImp.DoomBuilder.Config; +using CodeImp.DoomBuilder.Rendering; +using SlimDX.Direct3D9; +using System.Drawing.Imaging; +using System.Drawing.Drawing2D; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Windows; + +#endregion + +namespace CodeImp.DoomBuilder.Controls +{ + public class FlatSelectorControl : ImageSelectorControl + { + // Setup + public override void Initialize() + { + base.Initialize(); + + // Fill autocomplete list + name.AutoCompleteCustomSource.AddRange(General.Map.Data.FlatNames.ToArray()); + } + + // This finds the image we need for the given flat name + protected override Image FindImage(string imagename) + { + // Check if name is a "none" texture + if ((imagename.Length < 1) || (imagename[0] == '-')) + { + // Flat required! + return CodeImp.DoomBuilder.Properties.Resources.MissingTexture; + } + else + { + // Set the image + return General.Map.Data.GetFlatImage(imagename).GetPreview(); + } + } + + // This browses for a flat + protected override string BrowseImage(string imagename) + { + string result; + + // Browse for texture + result = FlatBrowserForm.Browse(this.ParentForm, imagename); + if (result != null) return result; else return imagename; + } + } +} diff --git a/Source/Core/Controls/ImageBrowserControl.Designer.cs b/Source/Core/Controls/ImageBrowserControl.Designer.cs new file mode 100644 index 0000000..d1c7613 --- /dev/null +++ b/Source/Core/Controls/ImageBrowserControl.Designer.cs @@ -0,0 +1,168 @@ +namespace CodeImp.DoomBuilder.Controls +{ + partial class ImageBrowserControl + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + CleanUp(); + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + this.splitter = new System.Windows.Forms.SplitContainer(); + this.list = new CodeImp.DoomBuilder.Controls.OptimizedListView(); + this.texturesize = new System.Windows.Forms.Label(); + this.texturesizelabel = new System.Windows.Forms.Label(); + this.objectname = new System.Windows.Forms.TextBox(); + this.label = new System.Windows.Forms.Label(); + this.refreshtimer = new System.Windows.Forms.Timer(this.components); + this.texturesizetimer = new System.Windows.Forms.Timer(this.components); + this.splitter.Panel1.SuspendLayout(); + this.splitter.Panel2.SuspendLayout(); + this.splitter.SuspendLayout(); + this.SuspendLayout(); + // + // splitter + // + this.splitter.Dock = System.Windows.Forms.DockStyle.Fill; + this.splitter.FixedPanel = System.Windows.Forms.FixedPanel.Panel2; + this.splitter.IsSplitterFixed = true; + this.splitter.Location = new System.Drawing.Point(0, 0); + this.splitter.Name = "splitter"; + this.splitter.Orientation = System.Windows.Forms.Orientation.Horizontal; + // + // splitter.Panel1 + // + this.splitter.Panel1.Controls.Add(this.list); + // + // splitter.Panel2 + // + this.splitter.Panel2.Controls.Add(this.texturesize); + this.splitter.Panel2.Controls.Add(this.texturesizelabel); + this.splitter.Panel2.Controls.Add(this.objectname); + this.splitter.Panel2.Controls.Add(this.label); + this.splitter.Size = new System.Drawing.Size(518, 346); + this.splitter.SplitterDistance = 312; + this.splitter.TabIndex = 0; + this.splitter.TabStop = false; + // + // list + // + this.list.Dock = System.Windows.Forms.DockStyle.Fill; + this.list.HideSelection = false; + this.list.Location = new System.Drawing.Point(0, 0); + this.list.MultiSelect = false; + this.list.Name = "list"; + this.list.OwnerDraw = true; + this.list.ShowItemToolTips = true; + this.list.Size = new System.Drawing.Size(518, 312); + this.list.TabIndex = 1; + this.list.TabStop = false; + this.list.TileSize = new System.Drawing.Size(90, 90); + this.list.UseCompatibleStateImageBehavior = false; + this.list.View = System.Windows.Forms.View.Tile; + this.list.DrawItem += new System.Windows.Forms.DrawListViewItemEventHandler(this.list_DrawItem); + this.list.DoubleClick += new System.EventHandler(this.list_DoubleClick); + this.list.ItemSelectionChanged += new System.Windows.Forms.ListViewItemSelectionChangedEventHandler(this.list_ItemSelectionChanged); + this.list.KeyDown += new System.Windows.Forms.KeyEventHandler(this.list_KeyDown); + // + // texturesize + // + this.texturesize.Location = new System.Drawing.Point(368, 13); + this.texturesize.Name = "texturesize"; + this.texturesize.Size = new System.Drawing.Size(271, 14); + this.texturesize.TabIndex = 2; + this.texturesize.Text = "1024 x 1024"; + this.texturesize.Visible = false; + // + // texturesizelabel + // + this.texturesizelabel.AutoSize = true; + this.texturesizelabel.Location = new System.Drawing.Point(331, 13); + this.texturesizelabel.Name = "texturesizelabel"; + this.texturesizelabel.Size = new System.Drawing.Size(31, 14); + this.texturesizelabel.TabIndex = 1; + this.texturesizelabel.Text = "Size:"; + this.texturesizelabel.Visible = false; + // + // objectname + // + this.objectname.CharacterCasing = System.Windows.Forms.CharacterCasing.Upper; + this.objectname.Location = new System.Drawing.Point(145, 10); + this.objectname.Name = "objectname"; + this.objectname.Size = new System.Drawing.Size(122, 20); + this.objectname.TabIndex = 0; + this.objectname.TabStop = false; + this.objectname.TextChanged += new System.EventHandler(this.objectname_TextChanged); + this.objectname.KeyDown += new System.Windows.Forms.KeyEventHandler(this.objectname_KeyDown); + // + // label + // + this.label.AutoSize = true; + this.label.Location = new System.Drawing.Point(1, 13); + this.label.Name = "label"; + this.label.Size = new System.Drawing.Size(138, 14); + this.label.TabIndex = 0; + this.label.Text = "Select or type object name:"; + // + // refreshtimer + // + this.refreshtimer.Interval = 500; + this.refreshtimer.Tick += new System.EventHandler(this.refreshtimer_Tick); + // + // texturesizetimer + // + this.texturesizetimer.Interval = 3; + this.texturesizetimer.Tick += new System.EventHandler(this.texturesizetimer_Tick); + // + // ImageBrowserControl + // + this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; + this.Controls.Add(this.splitter); + this.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.Name = "ImageBrowserControl"; + this.Size = new System.Drawing.Size(518, 346); + this.Resize += new System.EventHandler(this.ImageBrowserControl_Resize); + this.splitter.Panel1.ResumeLayout(false); + this.splitter.Panel2.ResumeLayout(false); + this.splitter.Panel2.PerformLayout(); + this.splitter.ResumeLayout(false); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.SplitContainer splitter; + private OptimizedListView list; + private System.Windows.Forms.Timer refreshtimer; + private System.Windows.Forms.TextBox objectname; + private System.Windows.Forms.Label label; + private System.Windows.Forms.Label texturesize; + private System.Windows.Forms.Label texturesizelabel; + private System.Windows.Forms.Timer texturesizetimer; + + } +} diff --git a/Source/Core/Controls/ImageBrowserControl.cs b/Source/Core/Controls/ImageBrowserControl.cs new file mode 100644 index 0000000..435b8ec --- /dev/null +++ b/Source/Core/Controls/ImageBrowserControl.cs @@ -0,0 +1,528 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Text; +using System.Windows.Forms; +using Microsoft.Win32; +using System.Diagnostics; +using CodeImp.DoomBuilder.Actions; +using CodeImp.DoomBuilder.Data; +using CodeImp.DoomBuilder.Config; +using CodeImp.DoomBuilder.Rendering; +using SlimDX.Direct3D9; +using System.Drawing.Imaging; +using System.Drawing.Drawing2D; + +#endregion + +namespace CodeImp.DoomBuilder.Controls +{ + internal partial class ImageBrowserControl : UserControl + { + #region ================== Constants + + #endregion + + #region ================== Delegates / Events + + public delegate void SelectedItemChangedDelegate(); + public delegate void SelectedItemDoubleClickDelegate(); + + public event SelectedItemChangedDelegate SelectedItemChanged; + public event SelectedItemDoubleClickDelegate SelectedItemDoubleClicked; + + #endregion + + #region ================== Variables + + // Properties + private bool preventselection; + + // States + private bool updating; + private int keepselected; + + // All items + private List items; + + // Items visible in the list + private List visibleitems; + + #endregion + + #region ================== Properties + + public bool PreventSelection { get { return preventselection; } set { preventselection = value; } } + public bool HideInputBox { get { return splitter.Panel2Collapsed; } set { splitter.Panel2Collapsed = value; } } + public string LabelText { get { return label.Text; } set { label.Text = value; objectname.Left = label.Right + label.Margin.Right + objectname.Margin.Left; } } + public ListViewItem SelectedItem { get { if (list.SelectedItems.Count > 0) return list.SelectedItems[0]; else return null; } } + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + public ImageBrowserControl() + { + // Initialize + InitializeComponent(); + items = new List(); + + // Move textbox with label + objectname.Left = label.Right + label.Margin.Right + objectname.Margin.Left; + } + + // This applies the application settings + public void ApplySettings() + { + // Force black background? + if (General.Settings.BlackBrowsers) + { + list.BackColor = Color.Black; + list.ForeColor = Color.White; + } + + // Size of preview images + if (General.Map != null) + list.TileSize = new Size(General.Map.Data.Previews.MaxImageWidth + 26, General.Map.Data.Previews.MaxImageHeight + 26); + } + + // This cleans everything up + public virtual void CleanUp() + { + // Stop refresh timer + refreshtimer.Enabled = false; + } + + #endregion + + #region ================== Rendering + + // Draw item + private void list_DrawItem(object sender, DrawListViewItemEventArgs e) + { + if (!updating) (e.Item as ImageBrowserItem).Draw(e.Graphics, e.Bounds); + } + + // Refresher + private void refreshtimer_Tick(object sender, EventArgs e) + { + bool allpreviewsloaded = true; + + // Go for all items + foreach (ImageBrowserItem i in list.Items) + { + // Check if there are still previews that are not loaded + allpreviewsloaded &= i.IsPreviewLoaded; + + // Items needs to be redrawn? + if (i.CheckRedrawNeeded()) + { + // Refresh item in list + //list.RedrawItems(i.Index, i.Index, false); + list.Invalidate(); + } + } + + // If all previews were loaded, stop this timer + if (allpreviewsloaded) refreshtimer.Stop(); + + UpdateTextureSizeLabel(); + } + + #endregion + + #region ================== Events + + // Name typed + private void objectname_TextChanged(object sender, EventArgs e) + { + // Update list + RefillList(false); + + // No item selected? + if (list.SelectedItems.Count == 0) + { + // Select first + SelectFirstItem(); + } + } + + // Key pressed in textbox + private void objectname_KeyDown(object sender, KeyEventArgs e) + { + // Check what key is pressed + switch (e.KeyData) + { + // Cursor keys + case Keys.Left: SelectNextItem(SearchDirectionHint.Left); e.SuppressKeyPress = true; break; + case Keys.Right: SelectNextItem(SearchDirectionHint.Right); e.SuppressKeyPress = true; break; + case Keys.Up: SelectNextItem(SearchDirectionHint.Up); e.SuppressKeyPress = true; break; + case Keys.Down: SelectNextItem(SearchDirectionHint.Down); e.SuppressKeyPress = true; break; + + // Tab + case Keys.Tab: GoToNextSameTexture(); e.SuppressKeyPress = true; break; + } + } + + // Key pressed in list + private void list_KeyDown(object sender, KeyEventArgs e) + { + if (e.KeyData == Keys.Tab) + { + GoToNextSameTexture(); + e.SuppressKeyPress = true; + } + } + + // Selection changed + private void list_ItemSelectionChanged(object sender, ListViewItemSelectionChangedEventArgs e) + { + // Prevent selecting? + if (preventselection) + { + foreach (ListViewItem i in list.SelectedItems) i.Selected = false; + } + else + { + // Raise event + if (SelectedItemChanged != null) SelectedItemChanged(); + } + + UpdateTextureSizeLabel(); + } + + // Doublelicking an item + private void list_DoubleClick(object sender, EventArgs e) + { + if (!preventselection && (list.SelectedItems.Count > 0)) + if (SelectedItemDoubleClicked != null) SelectedItemDoubleClicked(); + } + + // Control is resized + private void ImageBrowserControl_Resize(object sender, EventArgs e) + { + UpdateTextureSizeLabel(); + } + + // This hides the texture size label + private void texturesizetimer_Tick(object sender, EventArgs e) + { + texturesizetimer.Stop(); + texturesize.Visible = false; + texturesizelabel.Visible = false; + } + + #endregion + + #region ================== Methods + + // This selects the next texture with the same name as the selected texture + public void GoToNextSameTexture() + { + if (list.SelectedItems.Count > 0) + { + ListViewItem selected = list.SelectedItems[0]; + bool foundselected = false; + foreach (ListViewItem n in visibleitems) + { + if ((n.Text == selected.Text) && foundselected) + { + // This is the next item + n.Selected = true; + n.EnsureVisible(); + return; + } + + if (n == selected) + foundselected = true; + } + + // Start from the top + foreach (ListViewItem n in visibleitems) + { + if ((n.Text == selected.Text) && foundselected) + { + // This is the next item + n.Selected = true; + n.EnsureVisible(); + return; + } + } + } + } + + // This selects an item by name + public void SelectItem(string name, ListViewGroup preferredgroup) + { + ListViewItem lvi = null; + + // Not when selecting is prevented + if (preventselection) return; + + // Search in preferred group first + if (preferredgroup != null) + { + foreach (ListViewItem item in list.Items) + { + if (string.Compare(item.Text, name, true) == 0) + { + lvi = item; + if (item.Group == preferredgroup) break; + } + } + } + + // Select the item + if (lvi != null) + { + // Select this item + list.SelectedItems.Clear(); + lvi.Selected = true; + lvi.EnsureVisible(); + } + + UpdateTextureSizeLabel(); + } + + // This performs item sleection by keys + private void SelectNextItem(SearchDirectionHint dir) + { + ListViewItem lvi; + Point spos; + + // Not when selecting is prevented + if (preventselection) return; + + // Nothing selected? + if (list.SelectedItems.Count == 0) + { + // Select first + SelectFirstItem(); + } + else + { + // Get selected item + lvi = list.SelectedItems[0]; + Rectangle lvirect = list.GetItemRect(lvi.Index, ItemBoundsPortion.Entire); + spos = new Point(lvirect.Location.X + lvirect.Width / 2, lvirect.Y + lvirect.Height / 2); + + // Try finding 5 times in the given direction + for (int i = 0; i < 5; i++) + { + // Move point in given direction + switch (dir) + { + case SearchDirectionHint.Left: spos.X -= list.TileSize.Width / 2; break; + case SearchDirectionHint.Right: spos.X += list.TileSize.Width / 2; break; + case SearchDirectionHint.Up: spos.Y -= list.TileSize.Height / 2; break; + case SearchDirectionHint.Down: spos.Y += list.TileSize.Height / 2; break; + } + + // Test position + lvi = list.GetItemAt(spos.X, spos.Y); + if (lvi != null) + { + // Select item + list.SelectedItems.Clear(); + lvi.Selected = true; + break; + } + } + + // Make selection visible + if (list.SelectedItems.Count > 0) list.SelectedItems[0].EnsureVisible(); + } + + UpdateTextureSizeLabel(); + } + + // This selectes the first item + private void SelectFirstItem() + { + ListViewItem lvi; + + // Not when selecting is prevented + if (preventselection) return; + + // Select first + if (list.Items.Count > 0) + { + list.SelectedItems.Clear(); + lvi = list.GetItemAt(list.TileSize.Width / 2, list.TileSize.Height / 2); + if (lvi != null) + { + lvi.Selected = true; + lvi.EnsureVisible(); + } + } + + UpdateTextureSizeLabel(); + } + + // This adds a group + public ListViewGroup AddGroup(string name) + { + ListViewGroup grp = new ListViewGroup(name); + list.Groups.Add(grp); + return grp; + } + + // This begins adding items + public void BeginAdding(bool keepselectedindex) + { + if (keepselectedindex && (list.SelectedItems.Count > 0)) + keepselected = list.SelectedIndices[0]; + else + keepselected = -1; + + // Clean list + items.Clear(); + + // Stop updating + refreshtimer.Enabled = false; + } + + // This ends adding items + public void EndAdding() + { + // Fill list with items + RefillList(true); + + // Start updating + refreshtimer.Enabled = true; + } + + // This adds an item + public void Add(string text, ImageData image, object tag, ListViewGroup group) + { + ImageBrowserItem i = new ImageBrowserItem(text, image, tag); + i.ListGroup = group; + i.Group = group; + items.Add(i); + } + + // This adds an item + public void Add(string text, ImageData image, object tag, ListViewGroup group, string tooltiptext) + { + ImageBrowserItem i = new ImageBrowserItem(text, image, tag); + i.ListGroup = group; + i.Group = group; + i.ToolTipText = tooltiptext; + items.Add(i); + } + + // This fills the list based on the objectname filter + private void RefillList(bool selectfirst) + { + visibleitems = new List(); + + // Begin updating list + updating = true; + //list.SuspendLayout(); + list.BeginUpdate(); + + // Clear list first + // Group property of items will be set to null, we will restore it later + list.Items.Clear(); + + // Go for all items + foreach (ImageBrowserItem i in items) + { + // Add item if valid + if (ValidateItem(i)) + { + i.Group = i.ListGroup; + i.Selected = false; + visibleitems.Add(i); + } + } + + // Fill list + visibleitems.Sort(); + ListViewItem[] array = new ListViewItem[visibleitems.Count]; + for (int i = 0; i < visibleitems.Count; i++) array[i] = visibleitems[i]; + list.Items.AddRange(array); + + // Done updating list + updating = false; + list.EndUpdate(); + list.Invalidate(); + //list.ResumeLayout(); + + // Make selection? + if (!preventselection && (list.Items.Count > 0)) + { + // Select specific item? + if (keepselected > -1) + { + list.Items[keepselected].Selected = true; + list.Items[keepselected].EnsureVisible(); + } + // Select first item? + else if (selectfirst) + { + SelectFirstItem(); + } + } + + // Raise event + if ((SelectedItemChanged != null) && !preventselection) SelectedItemChanged(); + UpdateTextureSizeLabel(); + } + + // This validates an item + private bool ValidateItem(ImageBrowserItem i) + { + return i.Text.Contains(objectname.Text); + } + + // This sends the focus to the textbox + public void FocusTextbox() + { + objectname.Focus(); + } + + // This updates the texture size label + private void UpdateTextureSizeLabel() + { + if ((list.SelectedItems.Count == 0) || + (splitter.Panel2.ClientSize.Width < (texturesize.Location.X + texturesize.Size.Width))) + { + texturesizetimer.Start(); + } + else + { + texturesizetimer.Stop(); + ImageBrowserItem lvi = (list.SelectedItems[0] as ImageBrowserItem); + if (lvi.icon.IsPreviewLoaded) + texturesize.Text = lvi.icon.Width + " x " + lvi.icon.Height; + else + texturesize.Text = "unknown"; + texturesize.Visible = true; + texturesizelabel.Visible = true; + } + } + + #endregion + } +} diff --git a/Source/Core/Controls/ImageBrowserControl.resx b/Source/Core/Controls/ImageBrowserControl.resx new file mode 100644 index 0000000..29bb72a --- /dev/null +++ b/Source/Core/Controls/ImageBrowserControl.resx @@ -0,0 +1,147 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + True + + + True + + + True + + + True + + + True + + + True + + + 103, 17 + + + 215, 17 + + + True + + \ No newline at end of file diff --git a/Source/Core/Controls/ImageBrowserItem.cs b/Source/Core/Controls/ImageBrowserItem.cs new file mode 100644 index 0000000..eea4b2c --- /dev/null +++ b/Source/Core/Controls/ImageBrowserItem.cs @@ -0,0 +1,155 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Text; +using System.Windows.Forms; +using Microsoft.Win32; +using System.Diagnostics; +using CodeImp.DoomBuilder.Actions; +using CodeImp.DoomBuilder.Data; +using CodeImp.DoomBuilder.Config; +using CodeImp.DoomBuilder.Rendering; +using System.Drawing.Imaging; +using System.Drawing.Drawing2D; +using SlimDX.Direct3D9; +using SlimDX; + +#endregion + +namespace CodeImp.DoomBuilder.Controls +{ + internal class ImageBrowserItem : ListViewItem, IComparable + { + #region ================== Variables + + // Display image + public ImageData icon; + + // Group + private ListViewGroup listgroup; + + // Image cache + private bool imageloaded; + + #endregion + + #region ================== Properties + + public ListViewGroup ListGroup { get { return listgroup; } set { listgroup = value; } } + public bool IsPreviewLoaded { get { return imageloaded; } } + + #endregion + + #region ================== Constructor / Disposer + + // Constructors + public ImageBrowserItem(string text, ImageData icon, object tag) + { + // Initialize + this.Text = text; + this.icon = icon; + this.Tag = tag; + } + + #endregion + + #region ================== Methods + + // This checks if a redraw is needed + public bool CheckRedrawNeeded() + { + return (icon.IsPreviewLoaded != imageloaded); + } + + // This draws the images + public void Draw(Graphics g, Rectangle bounds) + { + Brush forecolor; + Brush backcolor; + + // Remember if the preview is loaded + imageloaded = icon.IsPreviewLoaded; + + // Drawing settings + g.CompositingQuality = CompositingQuality.HighSpeed; + g.InterpolationMode = InterpolationMode.NearestNeighbor; + g.SmoothingMode = SmoothingMode.HighSpeed; + g.PixelOffsetMode = PixelOffsetMode.None; + + // Determine coordinates + SizeF textsize = g.MeasureString(this.Text, this.ListView.Font, bounds.Width * 2); + Rectangle imagerect = new Rectangle(bounds.Left + ((bounds.Width - General.Map.Data.Previews.MaxImageWidth) >> 1), + bounds.Top + ((bounds.Height - General.Map.Data.Previews.MaxImageHeight - (int)textsize.Height) >> 1), + General.Map.Data.Previews.MaxImageWidth, General.Map.Data.Previews.MaxImageHeight); + PointF textpos = new PointF(bounds.Left + ((float)bounds.Width - textsize.Width) * 0.5f, bounds.Bottom - textsize.Height - 2); + + // Determine colors + if (this.Selected) + { + // Highlighted + backcolor = new LinearGradientBrush(new Point(0, bounds.Top - 1), new Point(0, bounds.Bottom + 1), + AdjustedColor(SystemColors.Highlight, 0.2f), + AdjustedColor(SystemColors.Highlight, -0.1f)); + forecolor = SystemBrushes.HighlightText; + } + else + { + // Normal + backcolor = new SolidBrush(base.ListView.BackColor); + forecolor = new SolidBrush(base.ListView.ForeColor); + } + + // Draw! + g.FillRectangle(backcolor, bounds); + icon.DrawPreview(g, imagerect.Location); + g.DrawString(this.Text, this.ListView.Font, forecolor, textpos); + } + + // This brightens or darkens a color + private Color AdjustedColor(Color c, float amount) + { + Color4 cc = new Color4(c); + + // Adjust color + cc.Red = Saturate((cc.Red * (1f + amount)) + (amount * 0.5f)); + cc.Green = Saturate((cc.Green * (1f + amount)) + (amount * 0.5f)); + cc.Blue = Saturate((cc.Blue * (1f + amount)) + (amount * 0.5f)); + + // Return result + return Color.FromArgb(cc.ToArgb()); + } + + // This clamps a value between 0 and 1 + private float Saturate(float v) + { + if (v < 0f) return 0f; else if (v > 1f) return 1f; else return v; + } + + // Comparer + public int CompareTo(ImageBrowserItem other) + { + return this.Text.CompareTo(other.Text); + } + + #endregion + } +} diff --git a/Source/Core/Controls/ImageSelectorControl.Designer.cs b/Source/Core/Controls/ImageSelectorControl.Designer.cs new file mode 100644 index 0000000..5ec0161 --- /dev/null +++ b/Source/Core/Controls/ImageSelectorControl.Designer.cs @@ -0,0 +1,85 @@ +namespace CodeImp.DoomBuilder.Controls +{ + partial class ImageSelectorControl + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + if (bmp != null) bmp.Dispose(); + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.preview = new System.Windows.Forms.Panel(); + this.name = new CodeImp.DoomBuilder.Controls.AutoSelectTextbox(); + this.SuspendLayout(); + // + // preview + // + this.preview.BackColor = System.Drawing.SystemColors.AppWorkspace; + this.preview.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Zoom; + this.preview.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D; + this.preview.Location = new System.Drawing.Point(0, 0); + this.preview.Name = "preview"; + this.preview.Size = new System.Drawing.Size(68, 60); + this.preview.TabIndex = 1; + this.preview.MouseLeave += new System.EventHandler(this.preview_MouseLeave); + this.preview.MouseMove += new System.Windows.Forms.MouseEventHandler(this.preview_MouseMove); + this.preview.Click += new System.EventHandler(this.preview_Click); + this.preview.MouseDown += new System.Windows.Forms.MouseEventHandler(this.preview_MouseDown); + this.preview.MouseUp += new System.Windows.Forms.MouseEventHandler(this.preview_MouseUp); + this.preview.MouseEnter += new System.EventHandler(this.preview_MouseEnter); + // + // name + // + this.name.AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.Suggest; + this.name.AutoCompleteSource = System.Windows.Forms.AutoCompleteSource.CustomSource; + this.name.CharacterCasing = System.Windows.Forms.CharacterCasing.Upper; + this.name.Location = new System.Drawing.Point(0, 64); + this.name.MaxLength = 8; + this.name.Name = "name"; + this.name.Size = new System.Drawing.Size(68, 20); + this.name.TabIndex = 2; + this.name.TextChanged += new System.EventHandler(this.name_TextChanged); + // + // ImageSelectorControl + // + this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; + this.Controls.Add(this.name); + this.Controls.Add(this.preview); + this.Name = "ImageSelectorControl"; + this.Size = new System.Drawing.Size(115, 136); + this.Layout += new System.Windows.Forms.LayoutEventHandler(this.ImageSelectorControl_Layout); + this.Resize += new System.EventHandler(this.ImageSelectorControl_Resize); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + protected System.Windows.Forms.Panel preview; + protected CodeImp.DoomBuilder.Controls.AutoSelectTextbox name; + + } +} diff --git a/Source/Core/Controls/ImageSelectorControl.cs b/Source/Core/Controls/ImageSelectorControl.cs new file mode 100644 index 0000000..a114024 --- /dev/null +++ b/Source/Core/Controls/ImageSelectorControl.cs @@ -0,0 +1,240 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Text; +using System.Windows.Forms; +using Microsoft.Win32; +using System.Diagnostics; +using CodeImp.DoomBuilder.Actions; +using CodeImp.DoomBuilder.Data; +using CodeImp.DoomBuilder.Config; +using CodeImp.DoomBuilder.Rendering; +using SlimDX; +using SlimDX.Direct3D9; +using System.Drawing.Imaging; +using System.Drawing.Drawing2D; + +#endregion + +namespace CodeImp.DoomBuilder.Controls +{ + /// + /// Abstract control that provides a list of images. + /// + public abstract partial class ImageSelectorControl : UserControl + { + #region ================== Variables + + private Bitmap bmp; + private bool ispressed; + private bool ismouseinside; + private MouseButtons button; + protected bool allowclear; + + #endregion + + #region ================== Properties + + public string TextureName { get { return name.Text; } set { name.Text = value; } } + + #endregion + + #region ================== Constructor / Destructor + + // Constructor + public ImageSelectorControl() + { + // Initialize + InitializeComponent(); + } + + // Setup + public virtual void Initialize() + { + // set the max length of texture names + name.MaxLength = General.Map.Config.MaxTextureNamelength; + } + + #endregion + + #region ================== Events + + // When resized + private void ImageSelectorControl_Resize(object sender, EventArgs e) + { + // Fixed size + preview.Width = this.ClientSize.Width; + preview.Height = this.ClientSize.Height - name.Height - 4; + name.Width = this.ClientSize.Width; + name.Top = this.ClientSize.Height - name.Height; + } + + // Layout change + private void ImageSelectorControl_Layout(object sender, LayoutEventArgs e) + { + ImageSelectorControl_Resize(sender, EventArgs.Empty); + } + + // Image clicked + private void preview_Click(object sender, EventArgs e) + { + ispressed = false; + preview.BackColor = SystemColors.Highlight; + ShowPreview(FindImage(name.Text)); + if (button == MouseButtons.Right) + { + if (allowclear) name.Text = "-"; + } + else if (button == MouseButtons.Left) + { + name.Text = BrowseImage(name.Text); + } + } + + // Name text changed + private void name_TextChanged(object sender, EventArgs e) + { + // Show it centered + ShowPreview(FindImage(name.Text)); + } + + // Mouse pressed + private void preview_MouseDown(object sender, MouseEventArgs e) + { + button = e.Button; + if ((button == MouseButtons.Left) || ((button == MouseButtons.Right) && allowclear)) + { + ispressed = true; + preview.BackColor = AdjustedColor(SystemColors.Highlight, 0.2f); + ShowPreview(FindImage(name.Text)); + } + } + + // Mouse released + private void preview_MouseUp(object sender, MouseEventArgs e) + { + ispressed = false; + ShowPreview(FindImage(name.Text)); + } + + // Mouse leaves + private void preview_MouseLeave(object sender, EventArgs e) + { + ispressed = false; + ismouseinside = false; + preview.BackColor = SystemColors.AppWorkspace; + } + + // Mouse enters + private void preview_MouseEnter(object sender, EventArgs e) + { + ismouseinside = true; + preview.BackColor = SystemColors.Highlight; + ShowPreview(FindImage(name.Text)); + } + + // Mouse moves + private void preview_MouseMove(object sender, MouseEventArgs e) + { + if (!ismouseinside) + { + ismouseinside = true; + preview.BackColor = SystemColors.Highlight; + ShowPreview(FindImage(name.Text)); + } + } + + #endregion + + #region ================== Methods + + // This refreshes the control + new public void Refresh() + { + ShowPreview(FindImage(name.Text)); + base.Refresh(); + } + + // This redraws the image preview + private void ShowPreview(Image image) + { + // Dispose old image + preview.BackgroundImage = null; + if (bmp != null) + { + bmp.Dispose(); + bmp = null; + } + + if (image != null) + { + // Show it centered + General.DisplayZoomedImage(preview, image); + preview.Refresh(); + } + } + + // This must determine and return the image to show + protected abstract Image FindImage(string imagename); + + // This must show the image browser and return the selected texture name + protected abstract string BrowseImage(string imagename); + + // This determines the result value + public string GetResult(string original) + { + // Anyting entered? + if (name.Text.Trim().Length > 0) + { + // Return the new value + return name.Text; + } + else + { + // Nothing given, keep original value + return original; + } + } + + // This brightens or darkens a color + private Color AdjustedColor(Color c, float amount) + { + Color4 cc = new Color4(c); + + // Adjust color + cc.Red = Saturate((cc.Red * (1f + amount)) + (amount * 0.5f)); + cc.Green = Saturate((cc.Green * (1f + amount)) + (amount * 0.5f)); + cc.Blue = Saturate((cc.Blue * (1f + amount)) + (amount * 0.5f)); + + // Return result + return Color.FromArgb(cc.ToArgb()); + } + + // This clamps a value between 0 and 1 + private float Saturate(float v) + { + if (v < 0f) return 0f; else if (v > 1f) return 1f; else return v; + } + + #endregion + } +} diff --git a/Source/Core/Controls/ImageSelectorControl.resx b/Source/Core/Controls/ImageSelectorControl.resx new file mode 100644 index 0000000..602fab3 --- /dev/null +++ b/Source/Core/Controls/ImageSelectorControl.resx @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + True + + + True + + + True + + \ No newline at end of file diff --git a/Source/Core/Controls/LinedefInfoPanel.Designer.cs b/Source/Core/Controls/LinedefInfoPanel.Designer.cs new file mode 100644 index 0000000..97d0bc4 --- /dev/null +++ b/Source/Core/Controls/LinedefInfoPanel.Designer.cs @@ -0,0 +1,595 @@ +namespace CodeImp.DoomBuilder.Controls +{ + partial class LinedefInfoPanel + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.Windows.Forms.Label label1; + System.Windows.Forms.Label label4; + System.Windows.Forms.Label label3; + System.Windows.Forms.Label label2; + this.infopanel = new System.Windows.Forms.GroupBox(); + this.unpegged = new System.Windows.Forms.Label(); + this.label6 = new System.Windows.Forms.Label(); + this.arg5 = new System.Windows.Forms.Label(); + this.arglbl5 = new System.Windows.Forms.Label(); + this.arglbl4 = new System.Windows.Forms.Label(); + this.arg4 = new System.Windows.Forms.Label(); + this.arglbl3 = new System.Windows.Forms.Label(); + this.arglbl2 = new System.Windows.Forms.Label(); + this.arg3 = new System.Windows.Forms.Label(); + this.arglbl1 = new System.Windows.Forms.Label(); + this.arg2 = new System.Windows.Forms.Label(); + this.backoffset = new System.Windows.Forms.Label(); + this.arg1 = new System.Windows.Forms.Label(); + this.backoffsetlabel = new System.Windows.Forms.Label(); + this.frontoffset = new System.Windows.Forms.Label(); + this.frontoffsetlabel = new System.Windows.Forms.Label(); + this.tag = new System.Windows.Forms.Label(); + this.angle = new System.Windows.Forms.Label(); + this.length = new System.Windows.Forms.Label(); + this.action = new System.Windows.Forms.Label(); + this.frontpanel = new System.Windows.Forms.GroupBox(); + this.frontsector = new System.Windows.Forms.Label(); + this.frontlowname = new System.Windows.Forms.Label(); + this.frontlowtex = new System.Windows.Forms.Panel(); + this.frontmidtex = new System.Windows.Forms.Panel(); + this.fronthighname = new System.Windows.Forms.Label(); + this.fronthightex = new System.Windows.Forms.Panel(); + this.frontmidname = new System.Windows.Forms.Label(); + this.backpanel = new System.Windows.Forms.GroupBox(); + this.backsector = new System.Windows.Forms.Label(); + this.backlowname = new System.Windows.Forms.Label(); + this.backlowtex = new System.Windows.Forms.Panel(); + this.backmidname = new System.Windows.Forms.Label(); + this.backmidtex = new System.Windows.Forms.Panel(); + this.backhighname = new System.Windows.Forms.Label(); + this.backhightex = new System.Windows.Forms.Panel(); + label1 = new System.Windows.Forms.Label(); + label4 = new System.Windows.Forms.Label(); + label3 = new System.Windows.Forms.Label(); + label2 = new System.Windows.Forms.Label(); + this.infopanel.SuspendLayout(); + this.frontpanel.SuspendLayout(); + this.backpanel.SuspendLayout(); + this.SuspendLayout(); + // + // label1 + // + label1.AutoSize = true; + label1.Location = new System.Drawing.Point(11, 19); + label1.Name = "label1"; + label1.Size = new System.Drawing.Size(41, 14); + label1.TabIndex = 0; + label1.Text = "Action:"; + // + // label4 + // + label4.AutoSize = true; + label4.Location = new System.Drawing.Point(24, 77); + label4.Name = "label4"; + label4.Size = new System.Drawing.Size(28, 14); + label4.TabIndex = 4; + label4.Text = "Tag:"; + // + // label3 + // + label3.AutoSize = true; + label3.Location = new System.Drawing.Point(14, 58); + label3.Name = "label3"; + label3.Size = new System.Drawing.Size(38, 14); + label3.TabIndex = 3; + label3.Text = "Angle:"; + // + // label2 + // + label2.AutoSize = true; + label2.Location = new System.Drawing.Point(9, 39); + label2.Name = "label2"; + label2.Size = new System.Drawing.Size(43, 14); + label2.TabIndex = 2; + label2.Text = "Length:"; + // + // infopanel + // + this.infopanel.Controls.Add(this.unpegged); + this.infopanel.Controls.Add(this.label6); + this.infopanel.Controls.Add(this.arg5); + this.infopanel.Controls.Add(this.arglbl5); + this.infopanel.Controls.Add(this.arglbl4); + this.infopanel.Controls.Add(this.arg4); + this.infopanel.Controls.Add(this.arglbl3); + this.infopanel.Controls.Add(this.arglbl2); + this.infopanel.Controls.Add(this.arg3); + this.infopanel.Controls.Add(this.arglbl1); + this.infopanel.Controls.Add(this.arg2); + this.infopanel.Controls.Add(this.backoffset); + this.infopanel.Controls.Add(this.arg1); + this.infopanel.Controls.Add(this.backoffsetlabel); + this.infopanel.Controls.Add(this.frontoffset); + this.infopanel.Controls.Add(this.frontoffsetlabel); + this.infopanel.Controls.Add(this.tag); + this.infopanel.Controls.Add(this.angle); + this.infopanel.Controls.Add(this.length); + this.infopanel.Controls.Add(label4); + this.infopanel.Controls.Add(label3); + this.infopanel.Controls.Add(label2); + this.infopanel.Controls.Add(this.action); + this.infopanel.Controls.Add(label1); + this.infopanel.Location = new System.Drawing.Point(0, 0); + this.infopanel.Name = "infopanel"; + this.infopanel.Size = new System.Drawing.Size(461, 100); + this.infopanel.TabIndex = 1; + this.infopanel.TabStop = false; + this.infopanel.Text = " Linedef "; + // + // unpegged + // + this.unpegged.AutoSize = true; + this.unpegged.Location = new System.Drawing.Point(180, 39); + this.unpegged.Name = "unpegged"; + this.unpegged.Size = new System.Drawing.Size(32, 14); + this.unpegged.TabIndex = 29; + this.unpegged.Text = "None"; + this.unpegged.UseMnemonic = false; + // + // label6 + // + this.label6.AutoSize = true; + this.label6.Location = new System.Drawing.Point(118, 39); + this.label6.Name = "label6"; + this.label6.Size = new System.Drawing.Size(59, 14); + this.label6.TabIndex = 28; + this.label6.Text = "Unpegged:"; + // + // arg5 + // + this.arg5.AutoEllipsis = true; + this.arg5.Location = new System.Drawing.Point(373, 79); + this.arg5.Name = "arg5"; + this.arg5.Size = new System.Drawing.Size(83, 14); + this.arg5.TabIndex = 27; + this.arg5.Text = "Arg 1:"; + this.arg5.UseMnemonic = false; + // + // arglbl5 + // + this.arglbl5.AutoEllipsis = true; + this.arglbl5.BackColor = System.Drawing.Color.Transparent; + this.arglbl5.Location = new System.Drawing.Point(246, 79); + this.arglbl5.Name = "arglbl5"; + this.arglbl5.Size = new System.Drawing.Size(121, 14); + this.arglbl5.TabIndex = 22; + this.arglbl5.Text = "Arg 1:"; + this.arglbl5.TextAlign = System.Drawing.ContentAlignment.TopRight; + this.arglbl5.UseMnemonic = false; + // + // arglbl4 + // + this.arglbl4.AutoEllipsis = true; + this.arglbl4.BackColor = System.Drawing.Color.Transparent; + this.arglbl4.Location = new System.Drawing.Point(246, 64); + this.arglbl4.Name = "arglbl4"; + this.arglbl4.Size = new System.Drawing.Size(121, 14); + this.arglbl4.TabIndex = 21; + this.arglbl4.Text = "Arg 1:"; + this.arglbl4.TextAlign = System.Drawing.ContentAlignment.TopRight; + this.arglbl4.UseMnemonic = false; + // + // arg4 + // + this.arg4.AutoEllipsis = true; + this.arg4.Location = new System.Drawing.Point(373, 64); + this.arg4.Name = "arg4"; + this.arg4.Size = new System.Drawing.Size(83, 14); + this.arg4.TabIndex = 26; + this.arg4.Text = "Arg 1:"; + this.arg4.UseMnemonic = false; + // + // arglbl3 + // + this.arglbl3.AutoEllipsis = true; + this.arglbl3.BackColor = System.Drawing.Color.Transparent; + this.arglbl3.Location = new System.Drawing.Point(246, 49); + this.arglbl3.Name = "arglbl3"; + this.arglbl3.Size = new System.Drawing.Size(121, 14); + this.arglbl3.TabIndex = 20; + this.arglbl3.Text = "Arg 1:"; + this.arglbl3.TextAlign = System.Drawing.ContentAlignment.TopRight; + this.arglbl3.UseMnemonic = false; + // + // arglbl2 + // + this.arglbl2.AutoEllipsis = true; + this.arglbl2.BackColor = System.Drawing.Color.Transparent; + this.arglbl2.Location = new System.Drawing.Point(246, 34); + this.arglbl2.Name = "arglbl2"; + this.arglbl2.Size = new System.Drawing.Size(121, 14); + this.arglbl2.TabIndex = 19; + this.arglbl2.Text = "Arg 1:"; + this.arglbl2.TextAlign = System.Drawing.ContentAlignment.TopRight; + this.arglbl2.UseMnemonic = false; + // + // arg3 + // + this.arg3.AutoEllipsis = true; + this.arg3.Location = new System.Drawing.Point(373, 49); + this.arg3.Name = "arg3"; + this.arg3.Size = new System.Drawing.Size(83, 14); + this.arg3.TabIndex = 25; + this.arg3.Text = "Arg 1:"; + this.arg3.UseMnemonic = false; + // + // arglbl1 + // + this.arglbl1.AutoEllipsis = true; + this.arglbl1.BackColor = System.Drawing.Color.Transparent; + this.arglbl1.Location = new System.Drawing.Point(246, 19); + this.arglbl1.Name = "arglbl1"; + this.arglbl1.Size = new System.Drawing.Size(121, 14); + this.arglbl1.TabIndex = 18; + this.arglbl1.Text = "Arg 1:"; + this.arglbl1.TextAlign = System.Drawing.ContentAlignment.TopRight; + this.arglbl1.UseMnemonic = false; + // + // arg2 + // + this.arg2.AutoEllipsis = true; + this.arg2.Location = new System.Drawing.Point(373, 34); + this.arg2.Name = "arg2"; + this.arg2.Size = new System.Drawing.Size(83, 14); + this.arg2.TabIndex = 24; + this.arg2.Text = "Arg 1:"; + this.arg2.UseMnemonic = false; + // + // backoffset + // + this.backoffset.AutoSize = true; + this.backoffset.Location = new System.Drawing.Point(180, 77); + this.backoffset.Name = "backoffset"; + this.backoffset.Size = new System.Drawing.Size(49, 14); + this.backoffset.TabIndex = 17; + this.backoffset.Text = "100, 100"; + // + // arg1 + // + this.arg1.AutoEllipsis = true; + this.arg1.Location = new System.Drawing.Point(373, 19); + this.arg1.Name = "arg1"; + this.arg1.Size = new System.Drawing.Size(83, 14); + this.arg1.TabIndex = 23; + this.arg1.Text = "Arg 1:"; + this.arg1.UseMnemonic = false; + // + // backoffsetlabel + // + this.backoffsetlabel.AutoSize = true; + this.backoffsetlabel.Location = new System.Drawing.Point(111, 77); + this.backoffsetlabel.Name = "backoffsetlabel"; + this.backoffsetlabel.Size = new System.Drawing.Size(66, 14); + this.backoffsetlabel.TabIndex = 14; + this.backoffsetlabel.Text = "Back offset:"; + // + // frontoffset + // + this.frontoffset.AutoSize = true; + this.frontoffset.Location = new System.Drawing.Point(180, 58); + this.frontoffset.Name = "frontoffset"; + this.frontoffset.Size = new System.Drawing.Size(49, 14); + this.frontoffset.TabIndex = 11; + this.frontoffset.Text = "100, 100"; + // + // frontoffsetlabel + // + this.frontoffsetlabel.AutoSize = true; + this.frontoffsetlabel.Location = new System.Drawing.Point(110, 58); + this.frontoffsetlabel.Name = "frontoffsetlabel"; + this.frontoffsetlabel.Size = new System.Drawing.Size(67, 14); + this.frontoffsetlabel.TabIndex = 8; + this.frontoffsetlabel.Text = "Front offset:"; + // + // tag + // + this.tag.AutoSize = true; + this.tag.Location = new System.Drawing.Point(55, 77); + this.tag.Name = "tag"; + this.tag.Size = new System.Drawing.Size(13, 14); + this.tag.TabIndex = 7; + this.tag.Text = "0"; + // + // angle + // + this.angle.AutoSize = true; + this.angle.Location = new System.Drawing.Point(55, 58); + this.angle.Name = "angle"; + this.angle.Size = new System.Drawing.Size(25, 14); + this.angle.TabIndex = 6; + this.angle.Text = "360"; + // + // length + // + this.length.AutoSize = true; + this.length.Location = new System.Drawing.Point(55, 39); + this.length.Name = "length"; + this.length.Size = new System.Drawing.Size(31, 14); + this.length.TabIndex = 5; + this.length.Text = "1024"; + // + // action + // + this.action.AutoEllipsis = true; + this.action.BackColor = System.Drawing.Color.Transparent; + this.action.Location = new System.Drawing.Point(55, 19); + this.action.Name = "action"; + this.action.Size = new System.Drawing.Size(205, 14); + this.action.TabIndex = 1; + this.action.Text = "0 - Big Door that goes Wobbly Wobbly"; + this.action.UseMnemonic = false; + // + // frontpanel + // + this.frontpanel.Controls.Add(this.frontsector); + this.frontpanel.Controls.Add(this.frontlowname); + this.frontpanel.Controls.Add(this.frontlowtex); + this.frontpanel.Controls.Add(this.frontmidtex); + this.frontpanel.Controls.Add(this.fronthighname); + this.frontpanel.Controls.Add(this.fronthightex); + this.frontpanel.Controls.Add(this.frontmidname); + this.frontpanel.Location = new System.Drawing.Point(467, 0); + this.frontpanel.Name = "frontpanel"; + this.frontpanel.Size = new System.Drawing.Size(257, 100); + this.frontpanel.TabIndex = 2; + this.frontpanel.TabStop = false; + this.frontpanel.Text = " Front "; + // + // frontsector + // + this.frontsector.AutoSize = true; + this.frontsector.BackColor = System.Drawing.SystemColors.Control; + this.frontsector.Location = new System.Drawing.Point(186, 0); + this.frontsector.Name = "frontsector"; + this.frontsector.Size = new System.Drawing.Size(60, 14); + this.frontsector.TabIndex = 6; + this.frontsector.Text = "Sector 666"; + // + // frontlowname + // + this.frontlowname.BackColor = System.Drawing.SystemColors.Control; + this.frontlowname.Location = new System.Drawing.Point(170, 80); + this.frontlowname.Name = "frontlowname"; + this.frontlowname.Size = new System.Drawing.Size(82, 13); + this.frontlowname.TabIndex = 5; + this.frontlowname.Text = "BROWNHUG"; + this.frontlowname.TextAlign = System.Drawing.ContentAlignment.TopCenter; + this.frontlowname.UseMnemonic = false; + // + // frontlowtex + // + this.frontlowtex.BackColor = System.Drawing.SystemColors.AppWorkspace; + this.frontlowtex.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Zoom; + this.frontlowtex.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D; + this.frontlowtex.Location = new System.Drawing.Point(177, 19); + this.frontlowtex.Name = "frontlowtex"; + this.frontlowtex.Size = new System.Drawing.Size(68, 60); + this.frontlowtex.TabIndex = 4; + // + // frontmidtex + // + this.frontmidtex.BackColor = System.Drawing.SystemColors.AppWorkspace; + this.frontmidtex.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Zoom; + this.frontmidtex.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D; + this.frontmidtex.Location = new System.Drawing.Point(95, 19); + this.frontmidtex.Name = "frontmidtex"; + this.frontmidtex.Size = new System.Drawing.Size(68, 60); + this.frontmidtex.TabIndex = 2; + // + // fronthighname + // + this.fronthighname.BackColor = System.Drawing.SystemColors.Control; + this.fronthighname.Location = new System.Drawing.Point(6, 80); + this.fronthighname.Name = "fronthighname"; + this.fronthighname.Size = new System.Drawing.Size(82, 13); + this.fronthighname.TabIndex = 1; + this.fronthighname.Text = "BROWNHUG"; + this.fronthighname.TextAlign = System.Drawing.ContentAlignment.TopCenter; + this.fronthighname.UseMnemonic = false; + // + // fronthightex + // + this.fronthightex.BackColor = System.Drawing.SystemColors.AppWorkspace; + this.fronthightex.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Zoom; + this.fronthightex.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D; + this.fronthightex.Location = new System.Drawing.Point(13, 19); + this.fronthightex.Name = "fronthightex"; + this.fronthightex.Size = new System.Drawing.Size(68, 60); + this.fronthightex.TabIndex = 0; + // + // frontmidname + // + this.frontmidname.BackColor = System.Drawing.SystemColors.Control; + this.frontmidname.Location = new System.Drawing.Point(88, 80); + this.frontmidname.Name = "frontmidname"; + this.frontmidname.Size = new System.Drawing.Size(82, 13); + this.frontmidname.TabIndex = 3; + this.frontmidname.Text = "BROWNHUG"; + this.frontmidname.TextAlign = System.Drawing.ContentAlignment.TopCenter; + this.frontmidname.UseMnemonic = false; + // + // backpanel + // + this.backpanel.Controls.Add(this.backsector); + this.backpanel.Controls.Add(this.backlowname); + this.backpanel.Controls.Add(this.backlowtex); + this.backpanel.Controls.Add(this.backmidname); + this.backpanel.Controls.Add(this.backmidtex); + this.backpanel.Controls.Add(this.backhighname); + this.backpanel.Controls.Add(this.backhightex); + this.backpanel.Location = new System.Drawing.Point(730, 0); + this.backpanel.Name = "backpanel"; + this.backpanel.Size = new System.Drawing.Size(257, 100); + this.backpanel.TabIndex = 3; + this.backpanel.TabStop = false; + this.backpanel.Text = " Back "; + // + // backsector + // + this.backsector.AutoSize = true; + this.backsector.BackColor = System.Drawing.SystemColors.Control; + this.backsector.Location = new System.Drawing.Point(186, 0); + this.backsector.Name = "backsector"; + this.backsector.Size = new System.Drawing.Size(60, 14); + this.backsector.TabIndex = 7; + this.backsector.Text = "Sector 666"; + // + // backlowname + // + this.backlowname.BackColor = System.Drawing.SystemColors.Control; + this.backlowname.Location = new System.Drawing.Point(170, 80); + this.backlowname.Name = "backlowname"; + this.backlowname.Size = new System.Drawing.Size(82, 13); + this.backlowname.TabIndex = 5; + this.backlowname.Text = "BROWNHUG"; + this.backlowname.TextAlign = System.Drawing.ContentAlignment.TopCenter; + this.backlowname.UseMnemonic = false; + // + // backlowtex + // + this.backlowtex.BackColor = System.Drawing.SystemColors.AppWorkspace; + this.backlowtex.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Zoom; + this.backlowtex.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D; + this.backlowtex.Location = new System.Drawing.Point(177, 19); + this.backlowtex.Name = "backlowtex"; + this.backlowtex.Size = new System.Drawing.Size(68, 60); + this.backlowtex.TabIndex = 4; + // + // backmidname + // + this.backmidname.BackColor = System.Drawing.SystemColors.Control; + this.backmidname.Location = new System.Drawing.Point(88, 80); + this.backmidname.Name = "backmidname"; + this.backmidname.Size = new System.Drawing.Size(82, 13); + this.backmidname.TabIndex = 3; + this.backmidname.Text = "BROWNHUG"; + this.backmidname.TextAlign = System.Drawing.ContentAlignment.TopCenter; + this.backmidname.UseMnemonic = false; + // + // backmidtex + // + this.backmidtex.BackColor = System.Drawing.SystemColors.AppWorkspace; + this.backmidtex.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Zoom; + this.backmidtex.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D; + this.backmidtex.Location = new System.Drawing.Point(95, 19); + this.backmidtex.Name = "backmidtex"; + this.backmidtex.Size = new System.Drawing.Size(68, 60); + this.backmidtex.TabIndex = 2; + // + // backhighname + // + this.backhighname.BackColor = System.Drawing.SystemColors.Control; + this.backhighname.Location = new System.Drawing.Point(6, 80); + this.backhighname.Name = "backhighname"; + this.backhighname.Size = new System.Drawing.Size(82, 13); + this.backhighname.TabIndex = 1; + this.backhighname.Text = "BROWNHUG"; + this.backhighname.TextAlign = System.Drawing.ContentAlignment.TopCenter; + this.backhighname.UseMnemonic = false; + // + // backhightex + // + this.backhightex.BackColor = System.Drawing.SystemColors.AppWorkspace; + this.backhightex.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Zoom; + this.backhightex.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D; + this.backhightex.Location = new System.Drawing.Point(13, 19); + this.backhightex.Name = "backhightex"; + this.backhightex.Size = new System.Drawing.Size(68, 60); + this.backhightex.TabIndex = 0; + // + // LinedefInfoPanel + // + this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; + this.Controls.Add(this.backpanel); + this.Controls.Add(this.frontpanel); + this.Controls.Add(this.infopanel); + this.DoubleBuffered = true; + this.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.MaximumSize = new System.Drawing.Size(10000, 100); + this.MinimumSize = new System.Drawing.Size(100, 100); + this.Name = "LinedefInfoPanel"; + this.Size = new System.Drawing.Size(1047, 100); + this.infopanel.ResumeLayout(false); + this.infopanel.PerformLayout(); + this.frontpanel.ResumeLayout(false); + this.frontpanel.PerformLayout(); + this.backpanel.ResumeLayout(false); + this.backpanel.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.Label action; + private System.Windows.Forms.Label tag; + private System.Windows.Forms.Label angle; + private System.Windows.Forms.Label length; + private System.Windows.Forms.Label frontoffset; + private System.Windows.Forms.Label backoffset; + private System.Windows.Forms.Panel fronthightex; + private System.Windows.Forms.Label frontlowname; + private System.Windows.Forms.Panel frontlowtex; + private System.Windows.Forms.Label frontmidname; + private System.Windows.Forms.Panel frontmidtex; + private System.Windows.Forms.Label fronthighname; + private System.Windows.Forms.Label backlowname; + private System.Windows.Forms.Panel backlowtex; + private System.Windows.Forms.Label backmidname; + private System.Windows.Forms.Panel backmidtex; + private System.Windows.Forms.Label backhighname; + private System.Windows.Forms.Panel backhightex; + private System.Windows.Forms.GroupBox frontpanel; + private System.Windows.Forms.GroupBox backpanel; + private System.Windows.Forms.Label backoffsetlabel; + private System.Windows.Forms.Label frontoffsetlabel; + private System.Windows.Forms.Label arglbl5; + private System.Windows.Forms.Label arglbl4; + private System.Windows.Forms.Label arglbl3; + private System.Windows.Forms.Label arglbl2; + private System.Windows.Forms.Label arglbl1; + private System.Windows.Forms.Label arg5; + private System.Windows.Forms.Label arg4; + private System.Windows.Forms.Label arg3; + private System.Windows.Forms.Label arg2; + private System.Windows.Forms.Label arg1; + private System.Windows.Forms.GroupBox infopanel; + private System.Windows.Forms.Label unpegged; + private System.Windows.Forms.Label label6; + private System.Windows.Forms.Label frontsector; + private System.Windows.Forms.Label backsector; + + } +} diff --git a/Source/Core/Controls/LinedefInfoPanel.cs b/Source/Core/Controls/LinedefInfoPanel.cs new file mode 100644 index 0000000..1abda82 --- /dev/null +++ b/Source/Core/Controls/LinedefInfoPanel.cs @@ -0,0 +1,279 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Text; +using System.Windows.Forms; +using Microsoft.Win32; +using System.Diagnostics; +using CodeImp.DoomBuilder.Data; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Config; +using CodeImp.DoomBuilder.Types; +using CodeImp.DoomBuilder.IO; + +#endregion + +namespace CodeImp.DoomBuilder.Controls +{ + internal partial class LinedefInfoPanel : UserControl + { + private int hexenformatwidth; + private int doomformatwidth; + + // Constructor + public LinedefInfoPanel() + { + // Initialize + InitializeComponent(); + + // Hide stuff when in Doom format + hexenformatwidth = infopanel.Width; + doomformatwidth = infopanel.Width - 190; + } + + // This shows the info + public void ShowInfo(Linedef l) + { + TypeHandler th; + bool upperunpegged, lowerunpegged; + string peggedness; + + // Show/hide stuff depending on format + if (!General.Map.FormatInterface.HasActionArgs) + { + arglbl1.Visible = false; + arglbl2.Visible = false; + arglbl3.Visible = false; + arglbl4.Visible = false; + arglbl5.Visible = false; + arg1.Visible = false; + arg2.Visible = false; + arg3.Visible = false; + arg4.Visible = false; + arg5.Visible = false; + infopanel.Width = doomformatwidth; + } + else + { + arglbl1.Visible = true; + arglbl2.Visible = true; + arglbl3.Visible = true; + arglbl4.Visible = true; + arglbl5.Visible = true; + arg1.Visible = true; + arg2.Visible = true; + arg3.Visible = true; + arg4.Visible = true; + arg5.Visible = true; + infopanel.Width = hexenformatwidth; + } + + // Move panels + frontpanel.Left = infopanel.Left + infopanel.Width + infopanel.Margin.Right + frontpanel.Margin.Left; + backpanel.Left = frontpanel.Left + frontpanel.Width + frontpanel.Margin.Right + backpanel.Margin.Left; + + // Get line action information + LinedefActionInfo act = General.Map.Config.GetLinedefActionInfo(l.Action); + + // Determine peggedness + upperunpegged = l.IsFlagSet(General.Map.Config.UpperUnpeggedFlag); + lowerunpegged = l.IsFlagSet(General.Map.Config.LowerUnpeggedFlag); + if (upperunpegged && lowerunpegged) + peggedness = "Upper & Lower"; + else if (upperunpegged) + peggedness = "Upper"; + else if (lowerunpegged) + peggedness = "Lower"; + else + peggedness = "None"; + + // Linedef info + infopanel.Text = " Linedef " + l.Index + " "; + action.Text = act.ToString(); + length.Text = l.Length.ToString("0.##"); + angle.Text = l.AngleDeg.ToString() + "\u00B0"; + tag.Text = l.Tag.ToString(); + unpegged.Text = peggedness; + + // Arguments + arglbl1.Text = act.Args[0].Title + ":"; + arglbl2.Text = act.Args[1].Title + ":"; + arglbl3.Text = act.Args[2].Title + ":"; + arglbl4.Text = act.Args[3].Title + ":"; + arglbl5.Text = act.Args[4].Title + ":"; + arglbl1.Enabled = act.Args[0].Used; + arglbl2.Enabled = act.Args[1].Used; + arglbl3.Enabled = act.Args[2].Used; + arglbl4.Enabled = act.Args[3].Used; + arglbl5.Enabled = act.Args[4].Used; + arg1.Enabled = act.Args[0].Used; + arg2.Enabled = act.Args[1].Used; + arg3.Enabled = act.Args[2].Used; + arg4.Enabled = act.Args[3].Used; + arg5.Enabled = act.Args[4].Used; + th = General.Types.GetArgumentHandler(act.Args[0]); + th.SetValue(l.Args[0]); arg1.Text = th.GetStringValue(); + th = General.Types.GetArgumentHandler(act.Args[1]); + th.SetValue(l.Args[1]); arg2.Text = th.GetStringValue(); + th = General.Types.GetArgumentHandler(act.Args[2]); + th.SetValue(l.Args[2]); arg3.Text = th.GetStringValue(); + th = General.Types.GetArgumentHandler(act.Args[3]); + th.SetValue(l.Args[3]); arg4.Text = th.GetStringValue(); + th = General.Types.GetArgumentHandler(act.Args[4]); + th.SetValue(l.Args[4]); arg5.Text = th.GetStringValue(); + + // Front side available? + if (l.Front != null) + { + // Show sidedef info + frontpanel.Text = " Front Sidedef " + l.Front.Index + " "; + frontsector.Text = " Sector " + l.Front.Sector.Index; + frontsector.Visible = true; + frontoffset.Text = l.Front.OffsetX + ", " + l.Front.OffsetY; + fronthighname.Text = l.Front.HighTexture; + frontmidname.Text = l.Front.MiddleTexture; + frontlowname.Text = l.Front.LowTexture; + DisplaySidedefTexture(fronthightex, l.Front.HighTexture, l.Front.HighRequired()); + DisplaySidedefTexture(frontmidtex, l.Front.MiddleTexture, l.Front.MiddleRequired()); + DisplaySidedefTexture(frontlowtex, l.Front.LowTexture, l.Front.LowRequired()); + frontoffsetlabel.Enabled = true; + frontoffset.Enabled = true; + frontpanel.Enabled = true; + } + else + { + // Show no info + frontpanel.Text = " Front Sidedef "; + frontsector.Text = ""; + frontsector.Visible = false; + frontoffsetlabel.Enabled = false; + frontoffset.Enabled = false; + frontpanel.Enabled = false; + frontoffset.Text = "--, --"; + fronthighname.Text = ""; + frontmidname.Text = ""; + frontlowname.Text = ""; + fronthightex.BackgroundImage = null; + frontmidtex.BackgroundImage = null; + frontlowtex.BackgroundImage = null; + } + + // Back size available? + if (l.Back != null) + { + // Show sidedef info + backpanel.Text = " Back Sidedef " + l.Back.Index + " "; + backsector.Text = " Sector " + l.Back.Sector.Index; + backsector.Visible = true; + backoffset.Text = l.Back.OffsetX + ", " + l.Back.OffsetY; + backhighname.Text = l.Back.HighTexture; + backmidname.Text = l.Back.MiddleTexture; + backlowname.Text = l.Back.LowTexture; + DisplaySidedefTexture(backhightex, l.Back.HighTexture, l.Back.HighRequired()); + DisplaySidedefTexture(backmidtex, l.Back.MiddleTexture, l.Back.MiddleRequired()); + DisplaySidedefTexture(backlowtex, l.Back.LowTexture, l.Back.LowRequired()); + backoffsetlabel.Enabled = true; + backoffset.Enabled = true; + backpanel.Enabled = true; + } + else + { + // Show no info + backpanel.Text = " Back Sidedef "; + backsector.Text = ""; + backsector.Visible = false; + backoffsetlabel.Enabled = false; + backoffset.Enabled = false; + backpanel.Enabled = false; + backoffset.Text = "--, --"; + backhighname.Text = ""; + backmidname.Text = ""; + backlowname.Text = ""; + backhightex.BackgroundImage = null; + backmidtex.BackgroundImage = null; + backlowtex.BackgroundImage = null; + } + + // Position labels + frontsector.Left = frontlowtex.Right - frontsector.Width; + backsector.Left = backlowtex.Right - backsector.Width; + + // Show the whole thing + this.Show(); + this.Update(); + } + + // When visible changed + protected override void OnVisibleChanged(EventArgs e) + { + // Hiding panels + if (!this.Visible) + { + fronthightex.BackgroundImage = null; + frontmidtex.BackgroundImage = null; + frontlowtex.BackgroundImage = null; + backhightex.BackgroundImage = null; + backmidtex.BackgroundImage = null; + backlowtex.BackgroundImage = null; + } + + // Call base + base.OnVisibleChanged(e); + } + + // This shows a sidedef texture in a panel + private void DisplaySidedefTexture(Panel panel, string name, bool required) + { + // Check if name is a "none" texture + if ((name.Length < 1) || (name[0] == '-')) + { + // Determine image to show + if (required) + panel.BackgroundImage = CodeImp.DoomBuilder.Properties.Resources.MissingTexture; + else + panel.BackgroundImage = null; + } + else + { + // Set the image + panel.BackgroundImage = General.Map.Data.GetTextureImage(name).GetPreview(); + } + + // Image not null? + if (panel.BackgroundImage != null) + { + // Small enough to fit in panel? + if ((panel.BackgroundImage.Size.Width < panel.ClientRectangle.Width) && + (panel.BackgroundImage.Size.Height < panel.ClientRectangle.Height)) + { + // Display centered + panel.BackgroundImageLayout = ImageLayout.Center; + } + else + { + // Display zoomed + panel.BackgroundImageLayout = ImageLayout.Zoom; + } + } + } + } +} diff --git a/Source/Core/Controls/LinedefInfoPanel.resx b/Source/Core/Controls/LinedefInfoPanel.resx new file mode 100644 index 0000000..f600adf --- /dev/null +++ b/Source/Core/Controls/LinedefInfoPanel.resx @@ -0,0 +1,258 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + True + + + False + + + True + + + False + + + True + + + False + + + True + + + False + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + \ No newline at end of file diff --git a/Source/Core/Controls/NumericTextbox.cs b/Source/Core/Controls/NumericTextbox.cs new file mode 100644 index 0000000..f24cd39 --- /dev/null +++ b/Source/Core/Controls/NumericTextbox.cs @@ -0,0 +1,281 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Text; +using System.Globalization; +using System.Windows.Forms; +using CodeImp.DoomBuilder.Actions; +using CodeImp.DoomBuilder.Geometry; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Editing; + +#endregion + +namespace CodeImp.DoomBuilder.Controls +{ + public class NumericTextbox : AutoSelectTextbox + { + #region ================== Constants + + #endregion + + #region ================== Variables + + private bool allownegative = false; // Allow negative numbers + private bool allowrelative = false; // Allow ++ and -- prefix for relative changes + private bool allowdecimal = false; // Allow decimal (float) numbers + private bool controlpressed = false; + + #endregion + + #region ================== Properties + + public bool AllowNegative { get { return allownegative; } set { allownegative = value; } } + public bool AllowRelative { get { return allowrelative; } set { allowrelative = value; } } + public bool AllowDecimal { get { return allowdecimal; } set { allowdecimal = value; } } + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + public NumericTextbox() + { + this.ImeMode = ImeMode.Off; + } + + #endregion + + #region ================== Methods + + // Key pressed + protected override void OnKeyDown(KeyEventArgs e) + { + controlpressed = e.Control; + base.OnKeyDown(e); + } + + // Key released + protected override void OnKeyUp(KeyEventArgs e) + { + controlpressed = e.Control; + base.OnKeyUp(e); + } + + // When a key is pressed + protected override void OnKeyPress(KeyPressEventArgs e) + { + string allowedchars = "0123456789\b"; + string nonselectedtext; + string textpart; + int selectionpos; + int numprefixes; + char otherprefix; + + // Determine allowed chars + if (allownegative) allowedchars += CultureInfo.CurrentUICulture.NumberFormat.NegativeSign; + if (allowrelative) allowedchars += "+-"; + if (controlpressed) allowedchars += "\u0018\u0003\u0016"; + if (allowdecimal) allowedchars += CultureInfo.CurrentUICulture.NumberFormat.CurrencyDecimalSeparator; + + // Check if key is not allowed + if (allowedchars.IndexOf(e.KeyChar) == -1) + { + // Cancel this + e.Handled = true; + } + else + { + // Check if + or - is pressed + if ((e.KeyChar == '+') || (e.KeyChar == '-')) + { + // Determine non-selected text + if (this.SelectionLength > 0) + { + nonselectedtext = this.Text.Substring(0, this.SelectionStart) + + this.Text.Substring(this.SelectionStart + this.SelectionLength); + } + else if (this.SelectionLength < 0) + { + nonselectedtext = this.Text.Substring(0, this.SelectionStart + this.SelectionLength) + + this.Text.Substring(this.SelectionStart); + } + else + { + nonselectedtext = this.Text; + } + + // Not at the start? + selectionpos = this.SelectionStart - 1; + if (this.SelectionLength < 0) selectionpos = (this.SelectionStart + this.SelectionLength) - 1; + if (selectionpos > -1) + { + // Find any other characters before the insert position + textpart = this.Text.Substring(0, selectionpos + 1); + textpart = textpart.Replace("+", ""); + textpart = textpart.Replace("-", ""); + if (textpart.Length > 0) + { + // Cancel this + e.Handled = true; + } + } + + // Determine other prefix + if (e.KeyChar == '+') otherprefix = '-'; else otherprefix = '+'; + + // Limit the number of + and - allowed + numprefixes = nonselectedtext.Split(e.KeyChar, otherprefix).Length; + if (numprefixes > 2) + { + // Can't have more than 2 prefixes + e.Handled = true; + } + else if (numprefixes > 1) + { + // Must have 2 the same prefixes + if (this.Text.IndexOf(e.KeyChar) == -1) e.Handled = true; + + // Double prefix must be allowed + if (!allowrelative) e.Handled = true; + } + } + } + + // Call base + base.OnKeyPress(e); + } + + // Validate contents + protected override void OnValidating(CancelEventArgs e) + { + string textpart = this.Text; + + // Strip prefixes + textpart = textpart.Replace("+", ""); + if (!allownegative) textpart = textpart.Replace("-", ""); + + // No numbers left? + if (textpart.Length == 0) + { + // Make the textbox empty + this.Text = ""; + } + + // Call base + base.OnValidating(e); + } + + // This checks if the number is relative + public bool CheckIsRelative() + { + // Prefixed with ++ or --? + return (this.Text.StartsWith("++") || this.Text.StartsWith("--")); + } + + // This determines the result value + public int GetResult(int original) + { + string textpart = this.Text; + int result; + + // Strip prefixes + textpart = textpart.Replace("+", ""); + textpart = textpart.Replace("-", ""); + + // Any numbers left? + if (textpart.Length > 0) + { + // Prefixed with ++? + if (this.Text.StartsWith("++")) + { + // Add number to original + if (!int.TryParse(textpart, out result)) result = 0; + return original + result; + } + // Prefixed with --? + else if (this.Text.StartsWith("--")) + { + // Subtract number from original + if (!int.TryParse(textpart, out result)) result = 0; + int newvalue = original - result; + if (!allownegative && (newvalue < 0)) newvalue = 0; + return newvalue; + } + else + { + // Return the new value + return int.TryParse(this.Text, out result) ? result : original; + } + } + else + { + // Nothing given, keep original value + return original; + } + } + + // This determines the result value + public float GetResultFloat(float original) + { + string textpart = this.Text; + float result; + + // Strip prefixes + textpart = textpart.Replace("+", ""); + textpart = textpart.Replace("-", ""); + + // Any numbers left? + if (textpart.Length > 0) + { + // Prefixed with ++? + if (this.Text.StartsWith("++")) + { + // Add number to original + if (!float.TryParse(textpart, out result)) result = 0; + return original + result; + } + // Prefixed with --? + else if (this.Text.StartsWith("--")) + { + // Subtract number from original + if (!float.TryParse(textpart, out result)) result = 0; + float newvalue = original - result; + if (!allownegative && (newvalue < 0)) newvalue = 0; + return newvalue; + } + else + { + // Return the new value + return float.TryParse(this.Text, out result) ? result : original; + } + } + else + { + // Nothing given, keep original value + return original; + } + } + + #endregion + } +} diff --git a/Source/Core/Controls/OptimizedListView.cs b/Source/Core/Controls/OptimizedListView.cs new file mode 100644 index 0000000..b47ed84 --- /dev/null +++ b/Source/Core/Controls/OptimizedListView.cs @@ -0,0 +1,64 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Text; +using System.Globalization; +using System.Windows.Forms; +using CodeImp.DoomBuilder.Actions; +using CodeImp.DoomBuilder.Geometry; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Editing; + +#endregion + +namespace CodeImp.DoomBuilder.Controls +{ + public class OptimizedListView : ListView + { + #region ================== Constants + + #endregion + + #region ================== Variables + + #endregion + + #region ================== Properties + + #endregion + + #region ================== Constructor + + // Constructor + public OptimizedListView() : base() + { + this.DoubleBuffered = true; + SetStyle(ControlStyles.OptimizedDoubleBuffer, true); + } + + #endregion + + #region ================== Methods + + #endregion + } +} diff --git a/Source/Core/Controls/PasteOptionsControl.Designer.cs b/Source/Core/Controls/PasteOptionsControl.Designer.cs new file mode 100644 index 0000000..cff3828 --- /dev/null +++ b/Source/Core/Controls/PasteOptionsControl.Designer.cs @@ -0,0 +1,148 @@ +namespace CodeImp.DoomBuilder.Controls +{ + partial class PasteOptionsControl + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.sectorheightsgroup = new System.Windows.Forms.GroupBox(); + this.adjustheights = new System.Windows.Forms.CheckBox(); + this.tagsgroup = new System.Windows.Forms.GroupBox(); + this.removeactions = new System.Windows.Forms.CheckBox(); + this.removetags = new System.Windows.Forms.RadioButton(); + this.renumbertags = new System.Windows.Forms.RadioButton(); + this.keeptags = new System.Windows.Forms.RadioButton(); + this.sectorheightsgroup.SuspendLayout(); + this.tagsgroup.SuspendLayout(); + this.SuspendLayout(); + // + // sectorheightsgroup + // + this.sectorheightsgroup.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.sectorheightsgroup.Controls.Add(this.adjustheights); + this.sectorheightsgroup.Location = new System.Drawing.Point(0, 164); + this.sectorheightsgroup.Name = "sectorheightsgroup"; + this.sectorheightsgroup.Size = new System.Drawing.Size(443, 83); + this.sectorheightsgroup.TabIndex = 3; + this.sectorheightsgroup.TabStop = false; + this.sectorheightsgroup.Text = " Floor and Ceiling heights "; + // + // adjustheights + // + this.adjustheights.AutoSize = true; + this.adjustheights.Location = new System.Drawing.Point(30, 39); + this.adjustheights.Name = "adjustheights"; + this.adjustheights.Size = new System.Drawing.Size(292, 17); + this.adjustheights.TabIndex = 0; + this.adjustheights.Text = "Adjust heights to match relatively with surrounding sector"; + this.adjustheights.UseVisualStyleBackColor = true; + // + // tagsgroup + // + this.tagsgroup.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.tagsgroup.Controls.Add(this.removeactions); + this.tagsgroup.Controls.Add(this.removetags); + this.tagsgroup.Controls.Add(this.renumbertags); + this.tagsgroup.Controls.Add(this.keeptags); + this.tagsgroup.Location = new System.Drawing.Point(0, 0); + this.tagsgroup.Name = "tagsgroup"; + this.tagsgroup.Size = new System.Drawing.Size(443, 158); + this.tagsgroup.TabIndex = 2; + this.tagsgroup.TabStop = false; + this.tagsgroup.Text = " Tags and Actions "; + // + // removeactions + // + this.removeactions.AutoSize = true; + this.removeactions.Location = new System.Drawing.Point(30, 117); + this.removeactions.Name = "removeactions"; + this.removeactions.Size = new System.Drawing.Size(116, 17); + this.removeactions.TabIndex = 3; + this.removeactions.Text = "Remove all actions"; + this.removeactions.UseVisualStyleBackColor = true; + // + // removetags + // + this.removetags.AutoSize = true; + this.removetags.Location = new System.Drawing.Point(30, 81); + this.removetags.Name = "removetags"; + this.removetags.Size = new System.Drawing.Size(101, 17); + this.removetags.TabIndex = 2; + this.removetags.TabStop = true; + this.removetags.Text = "Remove all tags"; + this.removetags.UseVisualStyleBackColor = true; + // + // renumbertags + // + this.renumbertags.AutoSize = true; + this.renumbertags.Location = new System.Drawing.Point(30, 57); + this.renumbertags.Name = "renumbertags"; + this.renumbertags.Size = new System.Drawing.Size(271, 17); + this.renumbertags.TabIndex = 1; + this.renumbertags.TabStop = true; + this.renumbertags.Text = "Renumber tags to resolve conflicts with existing tags"; + this.renumbertags.UseVisualStyleBackColor = true; + // + // keeptags + // + this.keeptags.AutoSize = true; + this.keeptags.Location = new System.Drawing.Point(30, 33); + this.keeptags.Name = "keeptags"; + this.keeptags.Size = new System.Drawing.Size(217, 17); + this.keeptags.TabIndex = 0; + this.keeptags.TabStop = true; + this.keeptags.Text = "Keep tags the same as they were copied"; + this.keeptags.UseVisualStyleBackColor = true; + // + // PasteOptionsControl + // + this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; + this.Controls.Add(this.sectorheightsgroup); + this.Controls.Add(this.tagsgroup); + this.Name = "PasteOptionsControl"; + this.Size = new System.Drawing.Size(443, 282); + this.sectorheightsgroup.ResumeLayout(false); + this.sectorheightsgroup.PerformLayout(); + this.tagsgroup.ResumeLayout(false); + this.tagsgroup.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.GroupBox sectorheightsgroup; + private System.Windows.Forms.CheckBox adjustheights; + private System.Windows.Forms.GroupBox tagsgroup; + private System.Windows.Forms.CheckBox removeactions; + private System.Windows.Forms.RadioButton removetags; + private System.Windows.Forms.RadioButton renumbertags; + private System.Windows.Forms.RadioButton keeptags; + } +} diff --git a/Source/Core/Controls/PasteOptionsControl.cs b/Source/Core/Controls/PasteOptionsControl.cs new file mode 100644 index 0000000..201c8fa --- /dev/null +++ b/Source/Core/Controls/PasteOptionsControl.cs @@ -0,0 +1,87 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Text; +using System.Globalization; +using System.Windows.Forms; +using CodeImp.DoomBuilder.Data; +using CodeImp.DoomBuilder.Windows; +using CodeImp.DoomBuilder.Config; + +#endregion + +namespace CodeImp.DoomBuilder.Controls +{ + public partial class PasteOptionsControl : UserControl + { + #region ================== Variables + + #endregion + + #region ================== Properties + + #endregion + + #region ================== Constructor + + // Constructor + public PasteOptionsControl() + { + InitializeComponent(); + } + + #endregion + + #region ================== Methods + + // This sets the options from the given PasteOptions + public void Setup(PasteOptions options) + { + // Setup controls + keeptags.Checked = (options.ChangeTags == 0); + renumbertags.Checked = (options.ChangeTags == 1); + removetags.Checked = (options.ChangeTags == 2); + removeactions.Checked = options.RemoveActions; + adjustheights.Checked = options.AdjustHeights; + } + + // This returns the options as set by the user + public PasteOptions GetOptions() + { + PasteOptions options = new PasteOptions(); + + // Collect settings + if (keeptags.Checked) + options.ChangeTags = 0; + else if (renumbertags.Checked) + options.ChangeTags = 1; + else if (removetags.Checked) + options.ChangeTags = 2; + options.RemoveActions = removeactions.Checked; + options.AdjustHeights = adjustheights.Checked; + + return options; + } + + #endregion + } +} diff --git a/Source/Core/Controls/PasteOptionsControl.resx b/Source/Core/Controls/PasteOptionsControl.resx new file mode 100644 index 0000000..0ecb7fa --- /dev/null +++ b/Source/Core/Controls/PasteOptionsControl.resx @@ -0,0 +1,144 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + \ No newline at end of file diff --git a/Source/Core/Controls/RenderTargetControl.cs b/Source/Core/Controls/RenderTargetControl.cs new file mode 100644 index 0000000..37f03a2 --- /dev/null +++ b/Source/Core/Controls/RenderTargetControl.cs @@ -0,0 +1,154 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Text; +using System.Globalization; +using System.Windows.Forms; +using CodeImp.DoomBuilder.Actions; +using CodeImp.DoomBuilder.Geometry; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Editing; + +#endregion + +namespace CodeImp.DoomBuilder.Controls +{ + public class RenderTargetControl : Panel + { + #region ================== Constants + + #endregion + + #region ================== Variables + + // Reference to image to render from + private Image img = null; + + #endregion + + #region ================== Properties + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + internal RenderTargetControl() + { + // Initialize + this.SetStyle(ControlStyles.FixedWidth, true); + this.SetStyle(ControlStyles.FixedHeight, true); + } + + // Disposer + protected override void Dispose(bool disposing) + { + // Clean up + + // Done + base.Dispose(disposing); + } + + #endregion + + #region ================== Overrides + + // Paint method + protected override void OnPaint(PaintEventArgs pe) + { + /* + // Copy area that needs to be redrawn + if(img != null) + { + pe.Graphics.FillRectangle(Brushes.Black, pe.ClipRectangle); + pe.Graphics.DrawImage(img, pe.ClipRectangle, pe.ClipRectangle, GraphicsUnit.Pixel); + + } + */ + + // Pass on to base + // Do we really want this? + base.RaisePaintEvent(this, pe); + } + + #endregion + + #region ================== Methods + + // This sets the render source + public void SetImageSource(Image srcimg) + { + // Set new source image + img = srcimg; + //this.Image = img; + } + + // This sets up the control to display the splash logo + public void SetSplashLogoDisplay() + { + // Remove render image + this.img = null; + + // Change display to show splash logo + this.SetStyle(ControlStyles.SupportsTransparentBackColor, false); + this.SetStyle(ControlStyles.ContainerControl, true); + this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true); + this.SetStyle(ControlStyles.UserPaint, true); + this.SetStyle(ControlStyles.AllPaintingInWmPaint, true); + this.SetStyle(ControlStyles.Opaque, false); + this.UpdateStyles(); + this.BackColor = System.Drawing.SystemColors.AppWorkspace; + this.BackgroundImage = global::CodeImp.DoomBuilder.Properties.Resources.Splash3_trans; + this.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Center; + //this.Image = null; + } + + // This sets up the control for manual rendering + public void SetManualRendering() + { + // Change display for rendering + /* + this.SetStyle(ControlStyles.SupportsTransparentBackColor, false); + this.SetStyle(ControlStyles.ContainerControl, true); + this.SetStyle(ControlStyles.OptimizedDoubleBuffer, false); + this.SetStyle(ControlStyles.UserPaint, true); + this.SetStyle(ControlStyles.AllPaintingInWmPaint, true); + this.SetStyle(ControlStyles.Opaque, true); + */ + this.SetStyle(ControlStyles.SupportsTransparentBackColor, false); + this.SetStyle(ControlStyles.ContainerControl, true); + this.SetStyle(ControlStyles.OptimizedDoubleBuffer, false); + this.SetStyle(ControlStyles.UserPaint, true); + this.SetStyle(ControlStyles.AllPaintingInWmPaint, true); + this.SetStyle(ControlStyles.Opaque, true); + this.UpdateStyles(); + this.BackColor = Color.Black; + this.BackgroundImage = null; + this.BackgroundImageLayout = System.Windows.Forms.ImageLayout.None; + //this.BackgroundImage = global::CodeImp.DoomBuilder.Properties.Resources.floor0_3; + //this.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Tile; + //this.Image = img; + } + + #endregion + } +} diff --git a/Source/Core/Controls/ResourceListEditor.Designer.cs b/Source/Core/Controls/ResourceListEditor.Designer.cs new file mode 100644 index 0000000..aa52066 --- /dev/null +++ b/Source/Core/Controls/ResourceListEditor.Designer.cs @@ -0,0 +1,229 @@ +namespace CodeImp.DoomBuilder.Controls +{ + partial class ResourceListEditor + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + System.Windows.Forms.SplitContainer buttonsbar2; + System.Windows.Forms.SplitContainer buttonsbar1; + System.Windows.Forms.ListViewItem listViewItem1 = new System.Windows.Forms.ListViewItem(new string[] { + "C:\\Windows\\Doom\\Doom2.wad"}, 3, System.Drawing.SystemColors.GrayText, System.Drawing.SystemColors.Window, null); + System.Windows.Forms.ListViewItem listViewItem2 = new System.Windows.Forms.ListViewItem(new string[] { + "C:\\My\\Little\\Textures\\"}, 2, System.Drawing.SystemColors.GrayText, System.Drawing.SystemColors.Window, null); + System.Windows.Forms.ListViewItem listViewItem3 = new System.Windows.Forms.ListViewItem("C:\\My\\Little\\Pony.wad", 1); + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(ResourceListEditor)); + this.editresource = new System.Windows.Forms.Button(); + this.deleteresource = new System.Windows.Forms.Button(); + this.addresource = new System.Windows.Forms.Button(); + this.splitContainer1 = new System.Windows.Forms.SplitContainer(); + this.resourceitems = new CodeImp.DoomBuilder.Controls.ResourceListView(); + this.column = new System.Windows.Forms.ColumnHeader(); + this.images = new System.Windows.Forms.ImageList(this.components); + buttonsbar2 = new System.Windows.Forms.SplitContainer(); + buttonsbar1 = new System.Windows.Forms.SplitContainer(); + buttonsbar2.Panel1.SuspendLayout(); + buttonsbar2.Panel2.SuspendLayout(); + buttonsbar2.SuspendLayout(); + buttonsbar1.Panel1.SuspendLayout(); + buttonsbar1.Panel2.SuspendLayout(); + buttonsbar1.SuspendLayout(); + this.splitContainer1.Panel1.SuspendLayout(); + this.splitContainer1.Panel2.SuspendLayout(); + this.splitContainer1.SuspendLayout(); + this.SuspendLayout(); + // + // buttonsbar2 + // + buttonsbar2.Dock = System.Windows.Forms.DockStyle.Fill; + buttonsbar2.IsSplitterFixed = true; + buttonsbar2.Location = new System.Drawing.Point(0, 0); + buttonsbar2.Name = "buttonsbar2"; + // + // buttonsbar2.Panel1 + // + buttonsbar2.Panel1.Controls.Add(this.editresource); + // + // buttonsbar2.Panel2 + // + buttonsbar2.Panel2.Controls.Add(this.deleteresource); + buttonsbar2.Size = new System.Drawing.Size(228, 24); + buttonsbar2.SplitterDistance = 136; + buttonsbar2.TabIndex = 0; + // + // editresource + // + this.editresource.Dock = System.Windows.Forms.DockStyle.Fill; + this.editresource.Enabled = false; + this.editresource.Location = new System.Drawing.Point(0, 0); + this.editresource.Name = "editresource"; + this.editresource.Size = new System.Drawing.Size(136, 24); + this.editresource.TabIndex = 0; + this.editresource.Text = "Resource options..."; + this.editresource.UseVisualStyleBackColor = true; + this.editresource.Click += new System.EventHandler(this.editresource_Click); + // + // deleteresource + // + this.deleteresource.Dock = System.Windows.Forms.DockStyle.Fill; + this.deleteresource.Enabled = false; + this.deleteresource.Location = new System.Drawing.Point(0, 0); + this.deleteresource.Name = "deleteresource"; + this.deleteresource.Size = new System.Drawing.Size(88, 24); + this.deleteresource.TabIndex = 0; + this.deleteresource.Text = "Remove"; + this.deleteresource.UseVisualStyleBackColor = true; + this.deleteresource.Click += new System.EventHandler(this.deleteresource_Click); + // + // buttonsbar1 + // + buttonsbar1.Dock = System.Windows.Forms.DockStyle.Fill; + buttonsbar1.IsSplitterFixed = true; + buttonsbar1.Location = new System.Drawing.Point(0, 0); + buttonsbar1.Name = "buttonsbar1"; + // + // buttonsbar1.Panel1 + // + buttonsbar1.Panel1.Controls.Add(this.addresource); + // + // buttonsbar1.Panel2 + // + buttonsbar1.Panel2.Controls.Add(buttonsbar2); + buttonsbar1.Size = new System.Drawing.Size(350, 24); + buttonsbar1.SplitterDistance = 118; + buttonsbar1.TabIndex = 0; + // + // addresource + // + this.addresource.Dock = System.Windows.Forms.DockStyle.Fill; + this.addresource.Location = new System.Drawing.Point(0, 0); + this.addresource.Name = "addresource"; + this.addresource.Size = new System.Drawing.Size(118, 24); + this.addresource.TabIndex = 0; + this.addresource.Text = "Add resource..."; + this.addresource.UseVisualStyleBackColor = true; + this.addresource.Click += new System.EventHandler(this.addresource_Click); + // + // splitContainer1 + // + this.splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill; + this.splitContainer1.FixedPanel = System.Windows.Forms.FixedPanel.Panel2; + this.splitContainer1.IsSplitterFixed = true; + this.splitContainer1.Location = new System.Drawing.Point(0, 0); + this.splitContainer1.Name = "splitContainer1"; + this.splitContainer1.Orientation = System.Windows.Forms.Orientation.Horizontal; + // + // splitContainer1.Panel1 + // + this.splitContainer1.Panel1.Controls.Add(this.resourceitems); + // + // splitContainer1.Panel2 + // + this.splitContainer1.Panel2.Controls.Add(buttonsbar1); + this.splitContainer1.Panel2MinSize = 24; + this.splitContainer1.Size = new System.Drawing.Size(350, 166); + this.splitContainer1.SplitterDistance = 138; + this.splitContainer1.TabIndex = 0; + // + // resourceitems + // + this.resourceitems.AllowDrop = true; + this.resourceitems.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.column}); + this.resourceitems.Dock = System.Windows.Forms.DockStyle.Fill; + this.resourceitems.FullRowSelect = true; + this.resourceitems.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.None; + this.resourceitems.HideSelection = false; + this.resourceitems.Items.AddRange(new System.Windows.Forms.ListViewItem[] { + listViewItem1, + listViewItem2, + listViewItem3}); + this.resourceitems.Location = new System.Drawing.Point(0, 0); + this.resourceitems.MultiSelect = false; + this.resourceitems.Name = "resourceitems"; + this.resourceitems.ShowGroups = false; + this.resourceitems.ShowItemToolTips = true; + this.resourceitems.Size = new System.Drawing.Size(350, 138); + this.resourceitems.SmallImageList = this.images; + this.resourceitems.TabIndex = 0; + this.resourceitems.UseCompatibleStateImageBehavior = false; + this.resourceitems.View = System.Windows.Forms.View.Details; + this.resourceitems.ClientSizeChanged += new System.EventHandler(this.resourceitems_ClientSizeChanged); + this.resourceitems.SizeChanged += new System.EventHandler(this.resources_SizeChanged); + this.resourceitems.DoubleClick += new System.EventHandler(this.resourceitems_DoubleClick); + this.resourceitems.DragDrop += new System.Windows.Forms.DragEventHandler(this.resourceitems_DragDrop); + this.resourceitems.ItemSelectionChanged += new System.Windows.Forms.ListViewItemSelectionChangedEventHandler(this.resourceitems_ItemSelectionChanged); + this.resourceitems.DragOver += new System.Windows.Forms.DragEventHandler(this.resourceitems_DragOver); + // + // column + // + this.column.Text = "Resource location"; + this.column.Width = 200; + // + // images + // + this.images.ImageStream = ((System.Windows.Forms.ImageListStreamer)(resources.GetObject("images.ImageStream"))); + this.images.TransparentColor = System.Drawing.Color.Transparent; + this.images.Images.SetKeyName(0, "Folder.ico"); + this.images.Images.SetKeyName(1, "File.ico"); + this.images.Images.SetKeyName(2, "PK3.ico"); + this.images.Images.SetKeyName(3, "FolderLocked.ico"); + this.images.Images.SetKeyName(4, "FileLocked.ico"); + this.images.Images.SetKeyName(5, "PK3Locked.ico"); + // + // ResourceListEditor + // + this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; + this.Controls.Add(this.splitContainer1); + this.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.Name = "ResourceListEditor"; + this.Size = new System.Drawing.Size(350, 166); + buttonsbar2.Panel1.ResumeLayout(false); + buttonsbar2.Panel2.ResumeLayout(false); + buttonsbar2.ResumeLayout(false); + buttonsbar1.Panel1.ResumeLayout(false); + buttonsbar1.Panel2.ResumeLayout(false); + buttonsbar1.ResumeLayout(false); + this.splitContainer1.Panel1.ResumeLayout(false); + this.splitContainer1.Panel2.ResumeLayout(false); + this.splitContainer1.ResumeLayout(false); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.Button deleteresource; + private System.Windows.Forms.Button editresource; + private System.Windows.Forms.Button addresource; + private CodeImp.DoomBuilder.Controls.ResourceListView resourceitems; + private System.Windows.Forms.ColumnHeader column; + private System.Windows.Forms.SplitContainer splitContainer1; + private System.Windows.Forms.ImageList images; + } +} diff --git a/Source/Core/Controls/ResourceListEditor.cs b/Source/Core/Controls/ResourceListEditor.cs new file mode 100644 index 0000000..434ab02 --- /dev/null +++ b/Source/Core/Controls/ResourceListEditor.cs @@ -0,0 +1,373 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Text; +using System.Globalization; +using System.Windows.Forms; +using CodeImp.DoomBuilder.Data; +using CodeImp.DoomBuilder.Windows; + +#endregion + +namespace CodeImp.DoomBuilder.Controls +{ + internal partial class ResourceListEditor : UserControl + { + #region ================== Delegates / Events + + public delegate void ContentChanged(); + + public event ContentChanged OnContentChanged; + + #endregion + + #region ================== Variables + + private Point dialogoffset = new Point(40, 20); + + #endregion + + #region ================== Properties + + public Point DialogOffset { get { return dialogoffset; } set { dialogoffset = value; } } + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + public ResourceListEditor() + { + // Initialize + InitializeComponent(); + ResizeColumnHeader(); + + // Start with a clear list + resourceitems.Items.Clear(); + } + + #endregion + + #region ================== Methods + + // This gets the icon index for a resource location type + private int GetIconIndex(int locationtype, bool locked) + { + int lockedaddition; + + // Locked? + if (locked) lockedaddition = (images.Images.Count / 2); + else lockedaddition = 0; + + // What type? + switch (locationtype) + { + case DataLocation.RESOURCE_DIRECTORY: + return 0 + lockedaddition; + + case DataLocation.RESOURCE_WAD: + return 1 + lockedaddition; + + case DataLocation.RESOURCE_PK3: + return 2 + lockedaddition; + + default: + return -1; + } + } + + // This will show a fixed list + public void FixedResourceLocationList(DataLocationList list) + { + // Start editing list + resourceitems.BeginUpdate(); + + // Go for all items + for (int i = resourceitems.Items.Count - 1; i >= 0; i--) + { + // Remove item if fixed + if (resourceitems.Items[i].ForeColor != SystemColors.WindowText) + resourceitems.Items.RemoveAt(i); + } + + // Go for all items + for (int i = list.Count - 1; i >= 0; i--) + { + // Add item as fixed + resourceitems.Items.Insert(0, new ListViewItem(list[i].location)); + resourceitems.Items[0].Tag = list[i]; + resourceitems.Items[0].ImageIndex = GetIconIndex(list[i].type, true); + + // Set disabled + resourceitems.Items[0].ForeColor = SystemColors.GrayText; + } + + // Done + resourceitems.EndUpdate(); + } + + // This will edit the given list + public void EditResourceLocationList(DataLocationList list) + { + // Start editing list + resourceitems.BeginUpdate(); + + // Scroll to top + if (resourceitems.Items.Count > 0) + resourceitems.TopItem = resourceitems.Items[0]; + + // Go for all items + for (int i = resourceitems.Items.Count - 1; i >= 0; i--) + { + // Remove item unless fixed + if (resourceitems.Items[i].ForeColor == SystemColors.WindowText) + resourceitems.Items.RemoveAt(i); + } + + // Go for all items + for (int i = 0; i < list.Count; i++) + { + // Add item + AddItem(list[i]); + } + + // Done + resourceitems.EndUpdate(); + ResizeColumnHeader(); + + // Raise content changed event + if (OnContentChanged != null) OnContentChanged(); + } + + // This adds a normal item + public void AddResourceLocation(DataLocation rl) + { + // Add it + AddItem(rl); + + // Raise content changed event + if (OnContentChanged != null) OnContentChanged(); + } + + // This adds a normal item + private void AddItem(DataLocation rl) + { + int index; + + // Start editing list + resourceitems.BeginUpdate(); + + // Add item + index = resourceitems.Items.Count; + resourceitems.Items.Add(new ListViewItem(rl.location)); + resourceitems.Items[index].Tag = rl; + resourceitems.Items[index].ImageIndex = GetIconIndex(rl.type, false); + + // Set normal color + resourceitems.Items[index].ForeColor = SystemColors.WindowText; + + // Done + resourceitems.EndUpdate(); + } + + // This fixes the column header in the list + private void ResizeColumnHeader() + { + // Resize column header to full extend + column.Width = resourceitems.ClientSize.Width - SystemInformation.VerticalScrollBarWidth; + } + + // When the resources list resizes + private void resources_SizeChanged(object sender, EventArgs e) + { + // Resize column header also + ResizeColumnHeader(); + } + + // Add a resource + private void addresource_Click(object sender, EventArgs e) + { + ResourceOptionsForm resoptions; + Rectangle startposition; + + // Open resource options dialog + resoptions = new ResourceOptionsForm(new DataLocation(), "Add Resource"); + resoptions.StartPosition = FormStartPosition.Manual; + startposition = new Rectangle(dialogoffset.X, dialogoffset.Y, 1, 1); + startposition = this.RectangleToScreen(startposition); + Screen screen = Screen.FromPoint(startposition.Location); + if (startposition.X + resoptions.Size.Width > screen.WorkingArea.Right) + startposition.X = screen.WorkingArea.Right - resoptions.Size.Width; + if (startposition.Y + resoptions.Size.Height > screen.WorkingArea.Bottom) + startposition.Y = screen.WorkingArea.Bottom - resoptions.Size.Height; + resoptions.Location = startposition.Location; + if (resoptions.ShowDialog(this) == DialogResult.OK) + { + // Add resource + AddItem(resoptions.ResourceLocation); + } + + // Raise content changed event + if (OnContentChanged != null) OnContentChanged(); + } + + // Edit resource + private void editresource_Click(object sender, EventArgs e) + { + ResourceOptionsForm resoptions; + Rectangle startposition; + ListViewItem selecteditem; + DataLocation rl; + + // Anything selected? + if (resourceitems.SelectedItems.Count > 0) + { + // Get selected item + selecteditem = resourceitems.SelectedItems[0]; + + // Open resource options dialog + resoptions = new ResourceOptionsForm((DataLocation)selecteditem.Tag, "Resource Options"); + resoptions.StartPosition = FormStartPosition.Manual; + startposition = new Rectangle(dialogoffset.X, dialogoffset.Y, 1, 1); + startposition = this.RectangleToScreen(startposition); + Screen screen = Screen.FromPoint(startposition.Location); + if (startposition.X + resoptions.Size.Width > screen.WorkingArea.Right) + startposition.X = screen.WorkingArea.Right - resoptions.Size.Width; + if (startposition.Y + resoptions.Size.Height > screen.WorkingArea.Bottom) + startposition.Y = screen.WorkingArea.Bottom - resoptions.Size.Height; + resoptions.Location = startposition.Location; + if (resoptions.ShowDialog(this) == DialogResult.OK) + { + // Start editing list + resourceitems.BeginUpdate(); + + // Update item + rl = resoptions.ResourceLocation; + selecteditem.Text = rl.location; + selecteditem.Tag = rl; + selecteditem.ImageIndex = GetIconIndex(rl.type, false); + + // Done + resourceitems.EndUpdate(); + + // Raise content changed event + if (OnContentChanged != null) OnContentChanged(); + } + } + } + + // Remove resource + private void deleteresource_Click(object sender, EventArgs e) + { + // Anything selected? + if (resourceitems.SelectedItems.Count > 0) + { + // Remove it + resourceitems.Items.Remove(resourceitems.SelectedItems[0]); + ResizeColumnHeader(); + + // Raise content changed event + if (OnContentChanged != null) OnContentChanged(); + } + } + + // Item selected + private void resourceitems_ItemSelectionChanged(object sender, ListViewItemSelectionChangedEventArgs e) + { + // Anything selected + if (resourceitems.SelectedItems.Count > 0) + { + // Go for all selected items + for (int i = resourceitems.SelectedItems.Count - 1; i >= 0; i--) + { + // Item grayed? Then deselect. + if (resourceitems.SelectedItems[i].ForeColor != SystemColors.WindowText) + resourceitems.SelectedItems[i].Selected = false; + } + } + + // Anything selected + if (resourceitems.SelectedItems.Count > 0) + { + // Enable buttons + editresource.Enabled = true; + deleteresource.Enabled = true; + } + else + { + // Disable buttons + editresource.Enabled = false; + deleteresource.Enabled = false; + } + } + + // When an item is double clicked + private void resourceitems_DoubleClick(object sender, EventArgs e) + { + // Click the edit resource button + if (editresource.Enabled) editresource_Click(sender, e); + } + + // Returns a list of the resources + public DataLocationList GetResources() + { + DataLocationList list = new DataLocationList(); + + // Go for all items + for (int i = 0; i < resourceitems.Items.Count; i++) + { + // Item not grayed? + if (resourceitems.Items[i].ForeColor == SystemColors.WindowText) + { + // Add item to list + list.Add((DataLocation)resourceitems.Items[i].Tag); + } + } + + // Return result + return list; + } + + // Item dragged + private void resourceitems_DragOver(object sender, DragEventArgs e) + { + // Raise content changed event + if (OnContentChanged != null) OnContentChanged(); + } + + // Item dropped + private void resourceitems_DragDrop(object sender, DragEventArgs e) + { + // Raise content changed event + if (OnContentChanged != null) OnContentChanged(); + } + + // Client size changed + private void resourceitems_ClientSizeChanged(object sender, EventArgs e) + { + // Resize column header + ResizeColumnHeader(); + } + + #endregion + } +} diff --git a/Source/Core/Controls/ResourceListEditor.resx b/Source/Core/Controls/ResourceListEditor.resx new file mode 100644 index 0000000..a04a202 --- /dev/null +++ b/Source/Core/Controls/ResourceListEditor.resx @@ -0,0 +1,254 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + True + + + False + + + True + + + True + + + True + + + False + + + True + + + True + + + 17, 17 + + + + AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj0yLjAuMC4w + LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAACZTeXN0 + ZW0uV2luZG93cy5Gb3Jtcy5JbWFnZUxpc3RTdHJlYW1lcgEAAAAERGF0YQcCAgAAAAkDAAAADwMAAACy + FgAAAk1TRnQBSQFMAgEBBgEAAQkBAAEEAQABEAEAARABAAT/ASEBAAj/AUIBTQE2BwABNgMAASgDAAFA + AwABIAMAAQEBAAEgBgABIDYAAQMClgH/AQMClgH/AQMCUwH/AQMCUwH/AQMCUwH/AQMCUwH/LAABAwKW + Af8BAwKWAf8BAwJTAf8BAwJTAf8BAwJTAf8BAwJTAf+UAAOFAf8DhQH/A4UB/wOFAf8BAwK5Af8BAwKW + Af8BAwKWAf8BAwKBAf8BAwKBAf8BAwJTAf8QAAOAAf8DgQH/A4AB/wOAAf8DgQH/A4EB/wOAAf8BAwK5 + Af8BAwKWAf8BAwKWAf8BAwKBAf8BAwKBAf8BAwJTAf+QAAOFAf8DhQH/A8YB/wPJAf8DxgH/AQMCuQH/ + A6AB/wN9Af8DfQH/AQMCgQH/AQMCUwH/DAADgAH/A2cB/wOAAf8DlQH/A8kB/wPaAf8D2gH/A7wB/wED + ArkB/wOoAf8DgQH/A4EB/wEDAoEB/wEDAlMB/4wAA4UB/wPBAf8DhQH/A+UB/wPhAf8D5QH/AQMCuQH/ + AQMD/wEDA/8BAwP/AQMCgQH/AQMCUwH/CAADgAH/A4EB/wOBAf8DgAH/A4EB/wOAAf8DgQH/A4AB/wOB + Af8BAwK5Af8BAwP/AQMD/wEDA/8BAwKBAf8BAwJTAf+IAAOFAf8DwQH/A9EB/wOFAf8D4QH/A+EB/wPl + Af8BAwK5Af8BAwKWAf8BAwKWAf8BAwKBAf8BAwKBAf8BAwJ2Af8QAAOPAf8DgAH/A4EB/wOAAf8DgQH/ + A4AB/wOBAf8BAwK5Af8BAwKWAf8BAwKWAf8BAwKBAf8BAwKBAf8BAwJ2Af+IAAOFAf8DhQH/A4UB/wPB + Af8D3gH/A94B/wPhAf8BAwK5Af8BAwJTAf8DsQH/Ax0B/wEDAoEB/wEDAlMB/wwAA48B/wPjAf8DjwH/ + A9UB/wPaAf8D3gH/A+MB/wOAAf8BAwK5Af8BAwJTAf8DsQH/A4EB/wEDAoEB/wEDAlMB/4gAA4UB/wPh + Af8D0QH/A9UB/wPZAf8D3gH/A+EB/wEDArkB/wEDAlMB/wMDAf8DHQH/AQMClgH/AQMCUwH/DAADjwH/ + A48B/wOPAf8D0QH/A9oB/wPeAf8D3gH/A4AB/wEDArkB/wEDAlMB/wMDAf8BIgEaARsB/wEDApYB/wED + AlMB/4gAA4UB/wPeAf8DzQH/A9EB/wPVAf8D2QH/A9kB/wPeAf8BAwK5Af8BAwK5Af8BAwKWAf8BAwJT + Af8QAAOPAf8D3gH/A9EB/wPRAf8D1QH/A9oB/wPaAf8DgAH/BAABAwK5Af8BAwK5Af8BAwKWAf8BAwJT + Af+MAAOFAf8D3gH/A80B/wPNAf8D0QH/A9UB/wPZAf8D2QH/A94B/wPZAf8DhQH/FAADjwH/A94B/wPN + Af8DzQH/A9EB/wPVAf8D2gH/A4AB/wQAA48B/wOPAf8DgQH/kAADhQH/A94B/wPJAf8DyQH/A80B/wPR + Af8D1QH/A9UB/wPZAf8D1QH/A4UB/xQAA48B/wPeAf8DyQH/A8kB/wPNAf8D0QH/A9UB/wOAAf8EAAOB + Af8D0QH/A6QB/5AAA4UB/wPeAf8DxgH/A8kB/wPJAf8DzQH/A9EB/wPRAf8D1QH/A9UB/wOFAf8UAAOP + Af8D3gH/A8UB/wPFAf8DyQH/A80B/wPNAf8DgQH/BAADjwH/A48B/wOAAf+QAAOFAf8D2QH/A8EB/wPB + Af8DxgH/A8kB/wPNAf8DzQH/A9UB/wPRAf8DhQH/FAADjwH/A9oB/wPBAf8DwQH/A8UB/wPJAf8DzQH/ + A4AB/wQAA4AB/wPRAf8DpAH/kAADhQH/A9kB/wPBAf8DxgH/A8YB/wPJAf8DzQH/A80B/wPRAf8D0QH/ + A4UB/xQAA48B/wOPAf8DjwH/A48B/wOPAf8DjwH/A48B/wOPAf8EAAOAAf8DoAH/A4EB/5AAA4UB/wP1 + Af8D4QH/A94B/wPeAf8D2QH/A9kB/wPZAf8D1QH/A9UB/wOFAf8QAAOAAf8DgAH/A4EB/wOAAf8DgQH/ + A4EB/wOBAf8DgQH/A4EB/wOAAf8DmgH/A58B/wNkAf8DgAH/jAADhQH/A4UB/wOFAf8DhQH/A4UB/wOF + Af8DhQH/A4UB/wOFAf8DhQH/A4UB/xQAA4EB/wNtAf8DlQH/A8kB/wPaAf8D2gH/A9oB/wPaAf8D2gH/ + A7wB/wOAAf8DgAH/A4AB/9AAA4AB/wOAAf8DgAH/A4AB/wOAAf8DgAH/A4AB/wOAAf8DgAH/A4AB/wOA + Af//ADEAAwUB/wMFAf8DBQH/AwUB/ywAAQMClgH/AQMClgH/AQMCUwH/AQMCUwH/AQMCUwH/AQMCUwH/ + WAADIgH/AyIB/wMiAf8DIgH/AyIB/wMiAf8DIgH/AyIB/xgAARUBCwEPAf8BFQELAQ8B/wEVAQsBDwH/ + ARUBCwEPAf8BFQELAQ8B/wEVAQsBDwH/ARUBCwEPAf8BMAEmASIB/wF3AXMBaQH/AWQBXwEyAf8BIQEc + AQ4B/wEVAQsBDwH/KAABAwK5Af8BAwKWAf8BAwKWAf8BAwKBAf8BAwKBAf8BAwJTAf8EAAGBAYgBkAH/ + AXQBgQGQAf8BdAKBAf8BZAKBAf8BZAF0AYEB/wFUAWwBdAH/AVQBXAFkAf8BRAFMAVQB/wE0AkQB/wI0 + AUQB/wEkASwBNAH/AyQB/wIkATQB/xwAAyIB/wMUAf8DsAH/A7EB/wOwAf8DrQH/A6sB/wOgAf8DIgH/ + FAABIAESAQ4B/wFEATIBEwH/AWgBWgE0Af8BnwGCATsB/wHbAccBkAH/AeYB1wG0Af8B5gHXAbQB/wG0 + Aa4BjwH/AXoBcAFNAf8BfgFwAUwB/wFdAVEBKgH/ASQBGAENAf8BFQELAQ8B/wQAA5cB/wORAf8DiQH/ + A4QB/wOBAf8DdAH/A20B/wNgAf8DUwH/AQMCuQH/A6AB/wN9Af8DfQH/AQMCgQH/AQMCUwH/BAABgQGI + AZAB/wGQAagBsAH/AZABqAGwAf8BFAGQAdAB/wEUAZAB0AH/ARQBkAHQAf8BFAGQAcAB/wEkAYgBwAH/ + ASQBgQGwAf8BJAGBAbAB/wE0AYEBoAH/ATQBgQGQAf8BNAFcAXQB/wOSAf8UAAMiAf8DqgH/AxQB/wPX + Af8D2QH/A9wB/wPfAf8D4QH/A88B/wMiAf8QAAENAQgBCgH/AQ0BCAEKAf8BDQEIAQoB/wENAQgBCgH/ + AQ0BCAEKAf8BDQEIAQoB/wENAQgBCgH/AQ0BCAEKAf8BDQEIAQoB/wEhARoBFgH/AXEBaAFUAf8BdwFm + ATkB/wEvASABDQH/ARUBCwEPAf8EAAOXAf8DsQH/A7EB/wOEAf8DhAH/A4QB/wOBAf8DhAH/A4EB/wED + ArkB/wEDA/8BAwP/AQMD/wEDAoEB/wEDAlMB/wQAAYEBiAGQAf8BdAHYAv8BkAGoAbAB/wGBAeAC/wF0 + AdAC/wFkAcgC/wFkAcgC/wFUAcAB8AH/AUQBsAHwAf8BRAGoAfAB/wE0AaAB4AH/ASQBkAHQAf8BNAF8 + AYEB/wFtAXUBewH/EAADIgH/A6oB/wPBAf8DFAH/A9UB/wPZAf8D3QH/A+EB/wPlAf8D0gH/AyIB/xgA + A2cB/wMFAf8DBQH/AwUB/wMFAf8DBQH/AwUB/wQAATsBMwEtAf8BcwFtAWMB/wEeARQBFwH/CAADngH/ + A74B/wOxAf8DywH/A74B/wO4Af8DuAH/A6sB/wOkAf8BAwK5Af8BAwKWAf8BAwKWAf8BAwKBAf8BAwKB + Af8BAwJ2Af8EAAGBAZABoAH/AXQB2AL/AZABqAGwAf8BkAHAAdAB/wGBAdgC/wF0AdAC/wF0AdAC/wFk + AcgC/wFkAcAC/wFUAbgB8AH/AUQBsAHwAf8BRAGoAfAB/wEkAYgB0AH/ATQBXAF0Af8DkgH/DAADIgH/ + AxQB/wMUAf8DqgH/A84B/wPTAf8D2AH/A9wB/wPgAf8D0QH/AyIB/xQAA2cB/wPYAf8DZwH/A8AB/wPO + Af8D0wH/A9gB/wMFAf8EAAE1ASsBKgH/A8AB/wOBAf8IAAOkAf8DvgH/A7EB/wO+Af8DxAH/A74B/wO+ + Af8DuAH/A7gB/wEDArkB/wEDAlMB/wOxAf8DHQH/AQMCgQH/AQMCUwH/BAABgQGQAaAB/wGBAdgB8AH/ + AXQB2AL/AZABqAGwAf8BgQHgAv8BgQHQAv8BdAHYAv8BdAHQAv8BdAHQAv8BZAHIAv8BVAHAAfAB/wFU + AbgB8AH/AUQBsAHwAf8BNAF8AYEB/wOBAf8MAAMiAf8D1QH/A8EB/wPEAf8DyQH/A88B/wPTAf8D1wH/ + A9sB/wPNAf8DIgH/FAADZwH/A2cB/wNnAf8DwAH/A8kB/wPPAf8D0wH/AwUB/wQAA2cB/wNnAf8BIQEY + ARoB/wgAA6QB/wPEAf8DvgH/A7EB/wPLAf8DxAH/A74B/wO+Af8DvgH/AQMCuQH/AQMCUwH/AwMB/wMd + Af8BAwKWAf8BAwJTAf8EAAGBAZgBoAH/AZAB4AHwAf8BdAHYAv8BkAGoAbAB/wGQAbgBwAH/AYEB2AL/ + AXQB2AL/AXQB2AL/AXQB2AL/AXQB0AL/AWQB0AL/AWQByAL/AVQBuAHwAf8BRAGgAeAB/wFdAXsBgQH/ + A5IB/wgAAyIB/wPTAf8DvAH/A8AB/wPFAf8DyQH/A80B/wPRAf8D1QH/A8wB/wMiAf8UAANnAf8D0wH/ + A7wB/wPAAf8DxQH/A8kB/wPNAf8DBQH/BAABLwElASYB/wPAAf8DgQH/CAADpAH/A8sB/wO+Af8DsQH/ + A7gB/wPEAf8DvgH/A74B/wO+Af8DvgH/AQMCuQH/AQMCuQH/AQMClgH/AQMCUwH/AUwBagF6Af8DpAH/ + AYEBmAGgAf8BkAHgAfAB/wGgAegC/wF0AdgC/wGQAagBsAH/AYEB4AL/AYEB4AL/AYEB4AL/AYEB4AL/ + AYEB4AL/AYEB4AL/AYEB4AL/AYEB2AL/AYEB2AL/AWQBqAHQAf8DgQH/CAADIgH/A9EB/wO3Af8DuwH/ + A8AB/wPEAf8DyAH/A8wB/wPRAf8DyQH/AyIB/xQAA2cB/wPRAf8DtwH/A7sB/wPAAf8DxAH/A8gB/wMF + Af8EAANnAf8DZwH/ARoBEwEUAf8IAAOkAf8DywH/A9gB/wO+Af8DsQH/A8sB/wPLAf8DywH/A8sB/wPL + Af8DywH/A8sB/wPEAf8DxAH/A6QB/wOJAf8BkAKgAf8BoAHoAfAB/wGgAegC/wGgAegC/wGQAagBsAH/ + AZABqAGwAf8BkAGoAbAB/wGQAagBsAH/AYEBoAGwAf8BgQGgAbAB/wGBAZgBoAH/AYEBmAGgAf8BgQGQ + AaAB/wGBAZABoAH/AYEBiAGQAf8BgQGIAZAB/wgAAyIB/wPRAf8DswH/A7UB/wO5Af8DvwH/A8MB/wPH + Af8DywH/A8YB/wMiAf8UAANnAf8D0QH/A7MB/wO1Af8DuQH/A78B/wPDAf8DBQH/BAABLwElASYB/wPA + Af8DgQH/CAADqwH/A9IB/wPYAf8D2AH/A7EB/wOxAf8DsQH/A7EB/wOrAf8DqwH/A6QB/wOkAf8DpAH/ + A6QB/wOeAf8DlwH/AZABoAGwAf8BoAHoAfAB/wGgAfAC/wGgAegC/wGgAegC/wGBAdgC/wF0AdgC/wF0 + AdgC/wF0AdgC/wF0AdgC/wF0AdgC/wF0AdgC/wGBAYgBkAH/FAADIgH/A9EB/wOtAf8DsQH/A7QB/wO5 + Af8DvAH/A8EB/wPFAf8DxAH/AyIB/xQAA2cB/wPRAf8DrQH/A7EB/wO0Af8DuQH/A7wB/wMFAf8EAANn + Af8DZwH/ARoBEwEUAf8IAAOxAf8D0gH/A9gB/wPYAf8D2AH/A8sB/wO+Af8DvgH/A74B/wO+Af8DvgH/ + A74B/wOXAf8MAAGQAaABsAH/AaAC8AH/AbAC8AH/AaAB8AL/AaAB6AL/AaAB6AL/AYEB2AL/AZACoAH/ + AYEBmAGgAf8BgQGYAaAB/wGBAZABoAH/AYECkAH/AYEBiAGQAf8UAAMiAf8DygH/A6gB/wOsAf8DsAH/ + A7QB/wO4Af8DvAH/A8AB/wPBAf8DIgH/FAADZwH/A8oB/wOoAf8DrAH/A7AB/wO0Af8DuAH/AwUB/wQA + AS8BJQEmAf8DwAH/A4EB/wgAA7EB/wPSAf8D2AH/A9gB/wPYAf8D2AH/A8QB/wOrAf8DpAH/A6QB/wOk + Af8DngH/A5cB/wwAAZABqAGwAf8BoAHQAeAB/wGwAvAB/wGwAvAB/wGgAfAC/wGgAegC/wGQAaABsAH/ + A5IB/ygAAyIB/wPLAf8DqQH/A6wB/wOwAf8DtAH/A7cB/wO7Af8DvQH/A8AB/wMiAf8UAANnAf8DZwH/ + A2cB/wNnAf8DZwH/A2cB/wNnAf8DZwH/BAABJAEcAR0B/wGFAYMBdQH/ASYBHQEcAf8IAAOxAf8DywH/ + A9gB/wPYAf8D2AH/A9gB/wOxAf8DpAH/JAABkAGoAbAB/wGQAagBsAH/AZABqAGwAf8BkAGoAbAB/wGQ + AagBsAH/A5IB/ywAAyIB/wPyAf8D1QH/A9IB/wPRAf8DzgH/A8sB/wPKAf8DxwH/A8YB/wMiAf8QAAEN + AQgBCgH/ARUBCwEPAf8BFQELAQ8B/wEVAQsBDwH/ARUBCwEPAf8BFQELAQ8B/wEVAQsBDwH/ARUBCwEP + Af8BFQELAQ8B/wEVAQsBDwH/AYEBewFsAf8BnQGRAVsB/wFAATABDgH/AwUB/wgAA7EB/wOxAf8DsQH/ + A7EB/wOxAf8DpAH/bAADIgH/AyIB/wMiAf8DIgH/AyIB/wMiAf8DIgH/AyIB/wMiAf8DIgH/AyIB/xQA + ARgBDgEMAf8BWAE8AQ4B/wGfAYIBOwH/AdsBxwGQAf8B5gHXAbQB/wHmAdcBtAH/AeYB1wG0Af8B5gHX + AbQB/wHgAdUBsQH/AbQBrgGPAf8BZQFZATYB/wEoARsBDQH/ARUBCwEPAf/QAAEPAQkBCwH/AQ0BCAEK + Af8BDQEIAQoB/wENAQgBCgH/AQ0BCAEKAf8BDQEIAQoB/wENAQgBCgH/AQ0BCAEKAf8BDQEIAQoB/wEP + AQkBCwH/ARUBCwEPAf9IAAFCAU0BPgcAAT4DAAEoAwABQAMAASADAAEBAQABAQYAAQEWAAP/AQAB/wGB + Af8BwAQAAfgBAQHgBQAB8AEBAcAFAAHgAQEBgAUAAcABAQHgBQABwAEBAcAFAAHAAQEBwAUAAcABAwHA + ASEEAAHAAQcBwAEjBAABwAEHAcABIwQAAcABBwHAASMEAAHAAQcBwAEjBAABwAEHAcABIwQAAcABBwGA + AQEEAAHAAQcBwAEBBAAC/wHgAQMEAAX/AcMB/wGBAv8B+AEHAeABAQH/AYEBAAEHAfABBwHAAQEBAAEB + AQABAwHgAQcBgAEBAQABAQEAAQMBwAEHAeABIwEAAQEBAAEBAcABBwHAASMBAAEBAQABAQHAAQcBwAEj + AQABAQIAAcABBwHAASMEAAHAAQcBwAEjBAABwAEHAcABIwMAAQcBwAEHAcABIwEAAQcBAAEHAcABBwHA + ASMBAAEHAQAB/wHAAQcBwAEjAQAB/wGBAf8BwAEHAYABAQGBA/8BwAEHAcABAQb/AeABAwL/Cw== + + + + True + + \ No newline at end of file diff --git a/Source/Core/Controls/ResourceListView.cs b/Source/Core/Controls/ResourceListView.cs new file mode 100644 index 0000000..fc085e8 --- /dev/null +++ b/Source/Core/Controls/ResourceListView.cs @@ -0,0 +1,286 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Text; +using System.Globalization; +using System.Windows.Forms; +using CodeImp.DoomBuilder.Actions; +using CodeImp.DoomBuilder.Geometry; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Editing; + +#endregion + +namespace CodeImp.DoomBuilder.Controls +{ + internal class ResourceListView : ListView + { + #region ================== Constants + + private const string DRAG_TYPE = "ReorderItems"; + + #endregion + + #region ================== Variables + + // List of items + private List dragitems; + + #endregion + + #region ================== Properties + + // Disable sorting + public new SortOrder Sorting { get { return SortOrder.None; } set { base.Sorting = SortOrder.None; } } + + #endregion + + #region ================== Constructor + + // Constructor + public ResourceListView() : base() + { + // List for dragged items + dragitems = new List(); + } + + #endregion + + #region ================== Overrides + + // When items are dropped + protected override void OnDragDrop(DragEventArgs e) + { + int dropindex, i; + ListViewItem insertatitem; + Point cp; + + // Pass on to base + base.OnDragDrop(e); + + // Leave when no items being dragged + if (dragitems.Count == 0) return; + + // Determine where to insert + cp = base.PointToClient(new Point(e.X, e.Y)); + insertatitem = base.GetItemAt(cp.X, cp.Y); + + // Leave when nowhere to insert or same as selected item + if ((insertatitem == null) || (dragitems.Contains(insertatitem))) return; + + // Leave when item is grayed + if (insertatitem.ForeColor != SystemColors.WindowText) return; + + // Begin updating + base.BeginUpdate(); + + // Determine index where to insert + dropindex = insertatitem.Index; + if (dropindex > dragitems[0].Index) dropindex++; + + // Deselect items + DeselectAll(); + + // Insert items + for (i = dragitems.Count - 1; i >= 0; i--) + { + // Insert a copy of the item here + base.Items.Insert(dropindex, (ListViewItem)dragitems[i].Clone()); + base.Items[dropindex].Selected = true; + } + + // Remove old items + foreach (ListViewItem lvi in dragitems) + { + // Remove item from list + base.Items.Remove(lvi); + } + + // Done + base.EndUpdate(); + dragitems.Clear(); + } + + // When items are dragged over + protected override void OnDragOver(DragEventArgs e) + { + int dropindex, i; + ListViewItem insertatitem; + Point cp; + + // Check if our data format is present + if (!e.Data.GetDataPresent(DataFormats.Text)) + { + e.Effect = DragDropEffects.None; + return; + } + + // Check if the data matches our data + String text = (String)e.Data.GetData(DRAG_TYPE.GetType()); + if (text.CompareTo(DRAG_TYPE + this.Name) == 0) + { + // Determine where to insert + cp = base.PointToClient(new Point(e.X, e.Y)); + insertatitem = base.GetItemAt(cp.X, cp.Y); + if (insertatitem == null) + { + // Cannot insert here + e.Effect = DragDropEffects.None; + return; + } + + // Item is one of the items being dragged? + if (dragitems.Contains(insertatitem)) + { + // Show move possibility, but dont do anything + e.Effect = DragDropEffects.Move; + insertatitem.EnsureVisible(); + return; + } + + // Check if item is grayed + if (insertatitem.ForeColor != SystemColors.WindowText) + { + // Cannot insert here + e.Effect = DragDropEffects.None; + insertatitem.EnsureVisible(); + return; + } + + // Pass on to base + base.OnDragOver(e); + + // Can insert here + e.Effect = DragDropEffects.Move; + insertatitem.EnsureVisible(); + + // Determine index where to insert + dropindex = insertatitem.Index; + if (dropindex > dragitems[0].Index) dropindex++; + + // Begin updating + base.BeginUpdate(); + + // Deselect items + DeselectAll(); + + // Insert items + for (i = dragitems.Count - 1; i >= 0; i--) + { + // Insert a copy of the item here + base.Items.Insert(dropindex, (ListViewItem)dragitems[i].Clone()); + base.Items[dropindex].Selected = true; + } + + // Remove old items + foreach (ListViewItem lvi in dragitems) + { + // Remove item from list + base.Items.Remove(lvi); + } + + // Copy selected items to the list + dragitems.Clear(); + foreach (ListViewItem lvi in base.SelectedItems) dragitems.Add(lvi); + + // Done + base.EndUpdate(); + } + else + { + // Cannot insert here + e.Effect = DragDropEffects.None; + } + } + + // When items are first dragged over + protected override void OnDragEnter(DragEventArgs e) + { + // Pass on to base + base.OnDragEnter(e); + + // Check if our data format is present + if (!e.Data.GetDataPresent(DataFormats.Text)) + { + // No effect + e.Effect = DragDropEffects.None; + return; + } + + // Check if the data matches our data + String text = (String)e.Data.GetData(DRAG_TYPE.GetType()); + if (text.CompareTo(DRAG_TYPE + base.Name) == 0) + { + // We're moving these items + e.Effect = DragDropEffects.Move; + } + else + { + // No effect + e.Effect = DragDropEffects.None; + } + } + + // When items are first dragged + protected override void OnItemDrag(ItemDragEventArgs e) + { + // Pass on to base + base.OnItemDrag(e); + + // Anything selected? + if (base.SelectedItems.Count > 0) + { + // Go for all selected items + for (int i = base.SelectedItems.Count - 1; i >= 0; i--) + { + // Item grayed? Then abort! + if (base.SelectedItems[i].ForeColor != SystemColors.WindowText) + return; + } + + // Copy selected items to the list + dragitems.Clear(); + foreach (ListViewItem lvi in base.SelectedItems) dragitems.Add(lvi); + + // Start drag operation + base.DoDragDrop(DRAG_TYPE + base.Name, DragDropEffects.Move); + } + } + + #endregion + + #region ================== Methods + + // This deselects all items + private void DeselectAll() + { + // Go for all selected items + for (int i = base.SelectedItems.Count - 1; i >= 0; i--) + { + // Item grayed? Then abort! + base.SelectedItems[i].Selected = false; + } + } + + #endregion + } +} diff --git a/Source/Core/Controls/ScintillaConstants.cs b/Source/Core/Controls/ScintillaConstants.cs new file mode 100644 index 0000000..9b83737 --- /dev/null +++ b/Source/Core/Controls/ScintillaConstants.cs @@ -0,0 +1,432 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Text; +using System.Windows.Forms; +using Microsoft.Win32; +using System.Diagnostics; +using CodeImp.DoomBuilder.Data; +using CodeImp.DoomBuilder.Map; + +#endregion + +namespace CodeImp.DoomBuilder.Controls +{ + internal struct NotifyHeader + { + // hwndFrom is really an environment specifc window handle or pointer + // but most clients of Scintilla.h do not have this type visible. + //WindowID hwndFrom; + public IntPtr hwndFrom; + public uint idFrom; + public uint code; + }; + + internal struct SCNotification + { + public NotifyHeader nmhdr; + public int position; // SCN_STYLENEEDED, SCN_MODIFIED, SCN_DWELLSTART, SCN_DWELLEND + public int ch; // SCN_CHARADDED, SCN_KEY + public int modifiers; // SCN_KEY + public int modificationType; // SCN_MODIFIED + public IntPtr text; // SCN_MODIFIED + public int length; // SCN_MODIFIED + public int linesAdded; // SCN_MODIFIED + public int message; // SCN_MACRORECORD + public IntPtr wParam; // SCN_MACRORECORD + public IntPtr lParam; // SCN_MACRORECORD + public int line; // SCN_MODIFIED + public int foldLevelNow; // SCN_MODIFIED + public int foldLevelPrev; // SCN_MODIFIED + public int margin; // SCN_MARGINCLICK + public int listType; // SCN_USERLISTSELECTION + public int x; // SCN_DWELLSTART, SCN_DWELLEND + public int y; // SCN_DWELLSTART, SCN_DWELLEND + }; + + internal enum ScriptWhiteSpace + { + Invisible = 0, + VisibleAlways = 1, + VisibleAfterIndent = 2 + } + + internal enum ScriptEndOfLine + { + CRLF = 0, + CR = 1, + LF = 2 + } + + internal enum ScriptMarkerSymbol + { + Circle = 0, + Roundrect = 1, + Arrow = 2, + SmallRect = 3, + ShortArrow = 4, + Empty = 5, + ArrowDown = 6, + Minus = 7, + Plus = 8, + VLine = 9, + LCorner = 10, + TCorner = 11, + BoxPlus = 12, + BoxPlusConnected = 13, + BoxMinus = 14, + BoxMinusConnected = 15, + LCornerCurve = 16, + TCornerCurve = 17, + CirclePlus = 18, + CirclePlusConnected = 19, + CircleMinus = 20, + CircleMinusConnected = 21, + Background = 22, + DotDotDot = 23, + Arrows = 24, + Pixmap = 25, + Character = 10000 + } + + internal enum ScriptMarkerOutline + { + FolderEnd = 25, + FolderOpenMid = 26, + FolderMidTail = 27, + FolderTail = 28, + FolderSub = 29, + Folder = 30, + FolderOpen = 31 + } + + internal enum ScriptMarginType + { + Symbol = 0, + Number = 1 + } + + internal enum ScriptStylesCommon + { + Default = 32, + LineNumber = 33, + BraceLight = 34, + BraceBad = 35, + ControlChar = 36, + IndentGuide = 37, + LastPredefined = 39, + Max = 127 + } + + internal enum ScriptCharacterSet + { + Ansi = 0, + Default = 1, + Baltic = 186, + ChineseBig5 = 136, + EastEurope = 238, + GB2312 = 134, + Greek = 161, + Hangul = 129, + Mac = 77, + Oem = 255, + Russian = 204, + Shiftjis = 128, + Symbol = 2, + Turkish = 162, + Johab = 130, + Hebrew = 177, + Arabic = 178, + Vietnamese = 163, + Thai = 222 + } + + internal enum ScriptCaseVisible + { + Mixed = 0, + Upper = 1, + Lower = 2 + } + + internal enum ScriptIndicatorStyle + { + Max = 7, + Plain = 0, + Squiggle = 1, + TT = 2, + Diagonal = 3, + Strike = 4, + Hidden = 5, + Box = 6 + } + + internal enum ScriptPrintOption + { + Normal = 0, + InvertLight = 1, + BlackOnWhite = 2, + ColourOnWhite = 3, + ColourOnWhiteDefaultBG = 4 + } + + internal enum ScriptFindOption + { + WholeWord = 2, + MatchCase = 4, + WordStart = 0x00100000, + RegExp = 0x00200000, + Posix = 0x00400000 + } + + internal enum ScriptFoldLevel + { + Base = 0x400, + WhiteFlag = 0x1000, + HeaderFlag = 0x2000, + BoxHeaderFlag = 0x4000, + BoxFooterFlag = 0x8000, + Contracted = 0x10000, + Unindent = 0x20000, + NumberMask = 0x0FFF + } + + internal enum ScriptFoldFlag + { + LineBefore_Expanded = 0x0002, + LineBefore_Contracted = 0x0004, + LineAfter_Expanded = 0x0008, + LineAfter_Contracted = 0x0010, + LevelNumbers = 0x0040, + Box = 0x0001 + } + + internal enum ScriptWrap + { + None = 0, + Word = 1 + } + + internal enum ScriptWrapVisualFlag + { + None = 0x0000, + End = 0x0001, + Start = 0x0002 + } + + internal enum ScriptWrapVisualLocation + { + Default = 0x0000, + EndByText = 0x0001, + StartByText = 0x0002 + } + + internal enum ScriptLineCache + { + None = 0, + Caret = 1, + Page = 2, + Document = 3 + } + + internal enum ScriptIdentGuides + { + None = 0, + Real = 1, + LookForward = 2, + LookBoth = 3 + } + + internal enum ScriptEdgeVisualStyle + { + None = 0, + Line = 1, + Background = 2 + } + + internal enum ScriptCursorShape + { + Normal = -1, + Wait = 4 + } + + internal enum ScriptCaretPolicy + { + Slop = 0x01, + Strict = 0x04, + Jumps = 0x10, + Even = 0x08 + } + + internal enum ScriptSelectionMode + { + Stream = 0, + Rectangle = 1, + Lines = 2 + } + + internal enum ScriptModificationFlags + { + InsertText = 0x1, + DeleteText = 0x2, + ChangeStyle = 0x4, + ChangeFold = 0x8, + User = 0x10, + Undo = 0x20, + Redo = 0x40, + StepInUndoRedo = 0x100, + ChangeMarker = 0x200, + BeforeInsert = 0x400, + BeforeDelete = 0x800 + } + + internal enum ScriptKeys + { + Down = 300, + Up = 301, + Left = 302, + Right = 303, + Home = 304, + End = 305, + Prior = 306, + Next = 307, + Delete = 308, + Insert = 309, + Escape = 7, + Back = 8, + Tab = 9, + Return = 13, + Add = 310, + Subtract = 311, + Divide = 312 + } + + internal enum ScriptKeyMod + { + Shift = 1, + Ctrl = 2, + Alt = 4 + } + + internal enum ScriptLexer + { + Container = 0, + Null = 1, + Python = 2, + Cpp = 3, + Html = 4, + Xml = 5, + Perl = 6, + Sql = 7, + Vb = 8, + Properties = 9, + Errorlist = 10, + Makefile = 11, + Batch = 12, + Xcode = 13, + Latex = 14, + Lua = 15, + Diff = 16, + Conf = 17, + Pascal = 18, + Ave = 19, + Ada = 20, + Lisp = 21, + Ruby = 22, + Eiffel = 23, + Eiffelkw = 24, + Tcl = 25, + NncronTab = 26, + Bullant = 27, + VBScript = 28, + Asp = 29, + Php = 30, + Baan = 31, + Matlab = 32, + Scriptol = 33, + Asm = 34, + CppNoCase = 35, + Fortran = 36, + F77 = 37, + Css = 38, + Pov = 39, + Lout = 40, + Escript = 41, + Ps = 42, + Nsis = 43, + Mmixal = 44, + Clw = 45, + Clwnocase = 46, + Lot = 47, + Yaml = 48, + Tex = 49, + Metapost = 50, + Powerbasic = 51, + Forth = 52, + Erlang = 53, + Octave = 54, + Mssql = 55, + Verilog = 56, + Kix = 57, + Gui4cli = 58, + Specman = 59, + Au3 = 60, + Apdl = 61, + Bash = 62, + Automatic = 1000 + } + + internal enum ScintillaEvents + { + StyleNeeded = 2000, + CharAdded = 2001, + SavePointReached = 2002, + SavePointLeft = 2003, + ModifyAttemptRO = 2004, + Key = 2005, + DoubleClick = 2006, + UpdateUI = 2007, + Modified = 2008, + MacroRecord = 2009, + MarginClick = 2010, + NeedShown = 2011, + Painted = 2013, + UserlistSelection = 2014, + UriDropped = 2015, + DwellStart = 2016, + DwellEnd = 2017, + Zoom = 2018, + HotspotClick = 2019, + HotspotDoubleClick = 2020, + CallTipClick = 2021 + } + + internal enum ScriptStyleType + { + PlainText = 0, + Keyword = 1, + Constant = 2, + Comment = 3, + Literal = 4, + LineNumber = 5 + } +} + diff --git a/Source/Core/Controls/ScintillaControl.cs b/Source/Core/Controls/ScintillaControl.cs new file mode 100644 index 0000000..804318a --- /dev/null +++ b/Source/Core/Controls/ScintillaControl.cs @@ -0,0 +1,4982 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Text; +using System.Windows.Forms; +using Microsoft.Win32; +using System.Diagnostics; +using CodeImp.DoomBuilder.Data; +using CodeImp.DoomBuilder.Map; +using System.Runtime.InteropServices; + +#endregion + +namespace CodeImp.DoomBuilder.Controls +{ + // This is only a wrapper for the Scintilla editor control. Most of this code is + // from ScintillaNET project, I only refactored it a bit. See the BuilderScriptControl + // for the script editor with Doom Builder features. + internal class ScintillaControl : Control + { + #region ================== API Declarations + + [DllImport(SCINTILLA_FILENAME, EntryPoint = "Scintilla_DirectFunction")] + private static extern int Perform(int ptr, UInt32 message, UInt32 wparam, UInt32 lparam); + + #endregion + + #region ================== Constants + + // Class library + private const string SCINTILLA_FILENAME = "Scintilla.dll"; + private const string SCINTILLA_CLASSNAME = "Scintilla"; + + // Styles + private const uint WS_CHILD = (uint)0x40000000L; + private const uint WS_VISIBLE = (uint)0x10000000L; + private const uint WS_TABSTOP = (uint)0x00010000L; + private const int WM_NOTIFY = 0x004E; + private const int WM_KEYDOWN = 0x0100; + private const int WM_KEYUP = 0x0101; + + #endregion + + #region ================== Delegates / Events + + public delegate void StyleNeededHandler(ScintillaControl pSender, int position); + public delegate void CharAddedHandler(ScintillaControl pSender, int ch); + public delegate void SavePointReachedHandler(ScintillaControl pSender); + public delegate void SavePointLeftHandler(ScintillaControl pSender); + public delegate void ModifyAttemptROHandler(ScintillaControl pSender); + public delegate void KeyHandler(ScintillaControl pSender, int ch, int modifiers); + public delegate void DoubleClickHandler(ScintillaControl pSender); + public delegate void UpdateUIHandler(ScintillaControl pSender); + public delegate void ModifiedHandler(ScintillaControl pSender, int position, int modificationType, string text, int length, int linesAdded, int line, int foldLevelNow, int foldLevelPrev); + public delegate void MacroRecordHandler(ScintillaControl pSender, int message, IntPtr wParam, IntPtr lParam); + public delegate void MarginClickHandler(ScintillaControl pSender, int modifiers, int position, int margin); + public delegate void NeedShownHandler(ScintillaControl pSender, int position, int length); + public delegate void PaintedHandler(ScintillaControl pSender); + public delegate void UserListSelectionHandler(ScintillaControl pSender, int listType, string text); + public delegate void URIDroppedHandler(ScintillaControl pSender, string text); + public delegate void DwellStartHandler(ScintillaControl pSender, int position); + public delegate void DwellEndHandler(ScintillaControl pSender, int position); + public delegate void ZoomHandler(ScintillaControl pSender); + public delegate void HotSpotClickHandler(ScintillaControl pSender, int modifiers, int position); + public delegate void HotSpotDoubleClickHandler(ScintillaControl pSender, int modifiers, int position); + public delegate void CallTipClickHandler(ScintillaControl pSender, int position); + public delegate void TextInsertedHandler(ScintillaControl pSender, int position, int length, int linesAdded); + public delegate void TextDeletedHandler(ScintillaControl pSender, int position, int length, int linesAdded); + public delegate void StyleChangedHandler(ScintillaControl pSender, int position, int length); + public delegate void FoldChangedHandler(ScintillaControl pSender, int line, int foldLevelNow, int foldLevelPrev); + public delegate void UserPerformedHandler(ScintillaControl pSender); + public delegate void UndoPerformedHandler(ScintillaControl pSender); + public delegate void RedoPerformedHandler(ScintillaControl pSender); + public delegate void LastStepInUndoRedoHandler(ScintillaControl pSender); + public delegate void MarkerChangedHandler(ScintillaControl pSender, int line); + public delegate void BeforeInsertHandler(ScintillaControl pSender, int position, int length); + public delegate void BeforeDeleteHandler(ScintillaControl pSender, int position, int length); + + public event StyleNeededHandler StyleNeeded; + public event CharAddedHandler CharAdded; + public event SavePointReachedHandler SavePointReached; + public event SavePointLeftHandler SavePointLeft; + public event ModifyAttemptROHandler ModifyAttemptRO; + public event KeyHandler Key; + public new event DoubleClickHandler DoubleClick; + public event UpdateUIHandler UpdateUI; + public event ModifiedHandler Modified; + public event MacroRecordHandler MacroRecord; + public event MarginClickHandler MarginClick; + public event NeedShownHandler NeedShown; + public event PaintedHandler Painted; + public event UserListSelectionHandler UserListSelection; + public event URIDroppedHandler URIDropped; + public event DwellStartHandler DwellStart; + public event DwellEndHandler DwellEnd; + public event ZoomHandler Zoom; + public event HotSpotClickHandler HotSpotClick; + public event HotSpotDoubleClickHandler HotSpotDoubleClick; + public event CallTipClickHandler CallTipClick; + public event TextInsertedHandler TextInserted; + public event TextDeletedHandler TextDeleted; + public event FoldChangedHandler FoldChanged; + public event UserPerformedHandler UserPerformed; + public event UndoPerformedHandler UndoPerformed; + public event RedoPerformedHandler RedoPerformed; + public event LastStepInUndoRedoHandler LastStepInUndoRedo; + public event MarkerChangedHandler MarkerChanged; + public event BeforeInsertHandler BeforeInsert; + public event BeforeDeleteHandler BeforeDelete; + public new event StyleChangedHandler StyleChanged; + + #endregion + + #region ================== Variables + + // Main objects + private IntPtr libraryptr = IntPtr.Zero; + private IntPtr controlptr = IntPtr.Zero; + private int directptr; + + // This ignores key combinations so that they are passed + // on to the other controls on the parent form + private Dictionary ignoredkeys; + + // States + private ScriptMarginType indexmargintype; + private ScriptIndicatorStyle indexindicatorstyle; + + #endregion + + #region ================== Properties + + /// + /// Are white space characters currently visible? + /// Returns one of SCWS_* constants. + /// + public ScriptWhiteSpace ViewWhitespace + { + get { return (ScriptWhiteSpace)ViewWS; } + set { ViewWS = (int)value; } + } + + /// + /// Retrieve the current end of line mode - one of CRLF, CR, or LF. + /// + public ScriptEndOfLine EndOfLineMode + { + get { return (ScriptEndOfLine)EOLMode; } + set { EOLMode = (int)value; } + } + + /// + /// The type of a margin. + /// + public ScriptMarginType MarginType { get { return indexmargintype; } } + + /// + /// The type of a margin. + /// + public ScriptIndicatorStyle IndicatorStyle { get { return indexindicatorstyle; } } + + /// + /// Are there any redoable actions in the undo history? + /// + /// + /// Autogenerated: IGEN09 + public bool CanRedo + { + get + { + return FastPerform(2016, 0, 0) != 0 ? true : false; + } + } + + /// + /// Is there an auto-completion list visible? + /// + /// + /// Autogenerated: IGEN09 + public bool IsAutoCActive + { + get + { + return FastPerform(2102, 0, 0) != 0 ? true : false; + } + } + + /// + /// Retrieve the position of the caret when the auto-completion list was displayed. + /// + /// + /// Autogenerated: IGEN10 + public int AutoCPosStart + { + get + { + return (int)FastPerform(2103, 0, 0); + } + } + + /// + /// Will a paste succeed? + /// + /// + /// Autogenerated: IGEN09 + public bool CanPaste + { + get + { + return FastPerform(2173, 0, 0) != 0 ? true : false; + } + } + + /// + /// Are there any undoable actions in the undo history? + /// + /// + /// Autogenerated: IGEN09 + public bool CanUndo + { + get + { + return FastPerform(2174, 0, 0) != 0 ? true : false; + } + } + + /// + /// Is there an active call tip? + /// + /// + /// Autogenerated: IGEN09 + public bool IsCallTipActive + { + get + { + return FastPerform(2202, 0, 0) != 0 ? true : false; + } + } + + /// + /// Retrieve the position where the caret was before displaying the call tip. + /// + /// + /// Autogenerated: IGEN10 + public int CallTipPosStart + { + get + { + return (int)FastPerform(2203, 0, 0); + } + } + + /// + /// Create a new document object. + /// Starts with reference count of 1 and not selected into editor. + /// + /// + /// Autogenerated: IGEN10 + public int CreateDocument + { + get + { + return (int)FastPerform(2375, 0, 0); + } + } + + /// + /// Get currently selected item position in the auto-completion list + /// + /// + /// Autogenerated: IGEN10 + public int AutoCGetCurrent + { + get + { + return (int)FastPerform(2445, 0, 0); + } + } + + /// + /// Width of the the auto-completion list + /// + public int AutoCMaximumWidth + { + get + { + return (int)FastPerform(2209, 0, 0); + } + + set + { + FastPerform(2208, (uint)value, 0); + } + } + + /// + /// Height of the the auto-completion list + /// + public int AutoCMaximumHeight + { + get + { + return (int)FastPerform(2211, 0, 0); + } + + set + { + FastPerform(2210, (uint)value, 0); + } + } + + /// + /// Spacing above a line + /// + public int ExtraAscent + { + get + { + return (int)FastPerform(2526, 0, 0); + } + + set + { + FastPerform(2525, (uint)value, 0); + } + } + + /// + /// Spacing below a line + /// + public int ExtraDescent + { + get + { + return (int)FastPerform(2528, 0, 0); + } + + set + { + FastPerform(2527, (uint)value, 0); + } + } + + /// + /// Returns the number of characters in the document. + /// + /// + /// Autogenerated: IGEN06 + public int Length + { + get + { + return (int)FastPerform(2006, 0, 0); + } + } + + /// + /// Returns the character byte at the position. + /// + /// + /// Autogenerated: IGEN18 + public int CharAt(int pos) + { + return (int)FastPerform(2007, (uint)pos, 0); + } + + /// + /// Returns the position of the caret. + /// + /// + /// Autogenerated: IGEN08 + public int CurrentPos + { + get + { + return (int)FastPerform(2008, 0, 0); + } + set + { + FastPerform(2141, (uint)value, 0); + } + } + + /// + /// Returns the position of the opposite end of the selection to the caret. + /// + /// + /// Autogenerated: IGEN08 + public int AnchorPosition + { + get + { + return (int)FastPerform(2009, 0, 0); + } + set + { + FastPerform(2026, (uint)value, 0); + } + } + + /// + /// Returns the style byte at the position. + /// + /// + /// Autogenerated: IGEN18 + public int StyleAt(int pos) + { + return (int)FastPerform(2010, (uint)pos, 0); + } + + /// + /// Is undo history being collected? + /// + /// + /// Autogenerated: IGEN07 + public bool IsUndoCollection + { + get + { + return FastPerform(2019, 0, 0) != 0; + } + set + { + FastPerform(2012, (uint)(value ? 1 : 0), 0); + } + } + + /// + /// Are white space characters currently visible? + /// Returns one of SCWS_* constants. + /// + /// + /// Autogenerated: IGEN08 + public int ViewWS + { + get + { + return (int)FastPerform(2020, 0, 0); + } + set + { + FastPerform(2021, (uint)value, 0); + } + } + + /// + /// Retrieve the position of the last correctly styled character. + /// + /// + /// Autogenerated: IGEN06 + public int EndStyled + { + get + { + return (int)FastPerform(2028, 0, 0); + } + } + + /// + /// Retrieve the current end of line mode - one of CRLF, CR, or LF. + /// + /// + /// Autogenerated: IGEN08 + public int EOLMode + { + get + { + return (int)FastPerform(2030, 0, 0); + } + set + { + FastPerform(2031, (uint)value, 0); + } + } + + /// + /// Is drawing done first into a buffer or direct to the screen? + /// + /// + /// Autogenerated: IGEN07 + public bool IsBufferedDraw + { + get + { + return FastPerform(2034, 0, 0) != 0; + } + set + { + FastPerform(2035, (uint)(value ? 1 : 0), 0); + } + } + + /// + /// Retrieve the visible size of a tab. + /// + /// + /// Autogenerated: IGEN08 + public int TabWidth + { + get + { + return (int)FastPerform(2121, 0, 0); + } + set + { + FastPerform(2036, (uint)value, 0); + } + } + + /// + /// Retrieve the type of a margin. + /// + /// + /// Autogenerated: IGEN29 + public int GetMarginTypeN(int margin) + { + return (int)FastPerform(2241, (uint)margin, 0); + } + + /// + /// Set a margin to be either numeric or symbolic. + /// + /// + /// Autogenerated: IGEN30 + public void SetMarginTypeN(int margin, int marginType) + { + FastPerform(2240, (uint)margin, (uint)marginType); + } + + /// + /// Retrieve the width of a margin in pixels. + /// + /// + /// Autogenerated: IGEN29 + public int GetMarginWidthN(int margin) + { + return (int)FastPerform(2243, (uint)margin, 0); + } + + /// + /// Set the width of a margin to a width expressed in pixels. + /// + /// + /// Autogenerated: IGEN30 + public void SetMarginWidthN(int margin, int pixelWidth) + { + FastPerform(2242, (uint)margin, (uint)pixelWidth); + } + + /// + /// Retrieve the marker mask of a margin. + /// + /// + /// Autogenerated: IGEN29 + public int GetMarginMaskN(int margin) + { + return (int)FastPerform(2245, (uint)margin, 0); + } + + /// + /// Set a mask that determines which markers are displayed in a margin. + /// + /// + /// Autogenerated: IGEN30 + public void SetMarginMaskN(int margin, int mask) + { + FastPerform(2244, (uint)margin, (uint)mask); + } + + /// + /// Retrieve the mouse click sensitivity of a margin. + /// + /// + /// Autogenerated: IGEN31 + public bool MarginSensitiveN(int margin) + { + return FastPerform(2247, (uint)margin, 0) != 0; + } + + /// + /// Make a margin sensitive or insensitive to mouse clicks. + /// + /// + /// Autogenerated: IGEN32 + public void MarginSensitiveN(int margin, bool sensitive) + { + FastPerform(2246, (uint)margin, (uint)(sensitive ? 1 : 0)); + } + + /// + /// Get the time in milliseconds that the caret is on and off. + /// + /// + /// Autogenerated: IGEN08 + public int CaretPeriod + { + get + { + return (int)FastPerform(2075, 0, 0); + } + set + { + FastPerform(2076, (uint)value, 0); + } + } + + /// + /// Retrieve the style of an indicator. + /// + /// + /// Autogenerated: IGEN29 + public int GetIndicStyle(int indic) + { + return (int)FastPerform(2081, (uint)indic, 0); + } + + /// + /// Set an indicator to plain, squiggle or TT. + /// + /// + /// Autogenerated: IGEN30 + public void SetIndicStyle(int indic, int style) + { + FastPerform(2080, (uint)indic, (uint)style); + } + + /// + /// Retrieve the foreground colour of an indicator. + /// + /// + /// Autogenerated: IGEN29 + public int GetIndicFore(int indic) + { + return (int)FastPerform(2083, (uint)indic, 0); + } + + /// + /// Set the foreground colour of an indicator. + /// + /// + /// Autogenerated: IGEN30 + public void SetIndicFore(int indic, int fore) + { + FastPerform(2082, (uint)indic, (uint)fore); + } + + /// + /// Retrieve number of bits in style bytes used to hold the lexical state. + /// + /// + /// Autogenerated: IGEN08 + public int StyleBits + { + get + { + return (int)FastPerform(2091, 0, 0); + } + set + { + FastPerform(2090, (uint)value, 0); + } + } + + /// + /// Retrieve the extra styling information for a line. + /// + /// + /// Autogenerated: IGEN29 + public int GetLineState(int line) + { + return (int)FastPerform(2093, (uint)line, 0); + } + + /// + /// Used to hold extra styling information for each line. + /// + /// + /// Autogenerated: IGEN30 + public void SetLineState(int line, int state) + { + FastPerform(2092, (uint)line, (uint)state); + } + + /// + /// Retrieve the last line number that has line state. + /// + /// + /// Autogenerated: IGEN06 + public int MaxLineState + { + get + { + return (int)FastPerform(2094, 0, 0); + } + } + + /// + /// Is the background of the line containing the caret in a different colour? + /// + /// + /// Autogenerated: IGEN07 + public bool IsCaretLineVisible + { + get + { + return FastPerform(2095, 0, 0) != 0; + } + set + { + FastPerform(2096, (uint)(value ? 1 : 0), 0); + } + } + + /// + /// Get the colour of the background of the line containing the caret. + /// + /// + /// Autogenerated: IGEN08 + public int CaretLineBack + { + get + { + return (int)FastPerform(2097, 0, 0); + } + set + { + FastPerform(2098, (uint)value, 0); + } + } + + /// + /// Retrieve the auto-completion list separator character. + /// + /// + /// Autogenerated: IGEN08 + public int AutoCSeparator + { + get + { + return (int)FastPerform(2107, 0, 0); + } + set + { + FastPerform(2106, (uint)value, 0); + } + } + + /// + /// Retrieve whether auto-completion cancelled by backspacing before start. + /// + /// + /// Autogenerated: IGEN07 + public bool IsAutoCGetCancelAtStart + { + get + { + return FastPerform(2111, 0, 0) != 0; + } + set + { + FastPerform(2110, (uint)(value ? 1 : 0), 0); + } + } + + /// + /// Retrieve whether a single item auto-completion list automatically choose the item. + /// + /// + /// Autogenerated: IGEN07 + public bool IsAutoCGetChooseSingle + { + get + { + return FastPerform(2114, 0, 0) != 0; + } + set + { + FastPerform(2113, (uint)(value ? 1 : 0), 0); + } + } + + /// + /// Retrieve state of ignore case flag. + /// + /// + /// Autogenerated: IGEN07 + public bool IsAutoCGetIgnoreCase + { + get + { + return FastPerform(2116, 0, 0) != 0; + } + set + { + FastPerform(2115, (uint)(value ? 1 : 0), 0); + } + } + + /// + /// Retrieve whether or not autocompletion is hidden automatically when nothing matches. + /// + /// + /// Autogenerated: IGEN07 + public bool IsAutoCGetAutoHide + { + get + { + return FastPerform(2119, 0, 0) != 0; + } + set + { + FastPerform(2118, (uint)(value ? 1 : 0), 0); + } + } + + /// + /// Retrieve whether or not autocompletion deletes any word characters + /// after the inserted text upon completion. + /// + /// + /// Autogenerated: IGEN07 + public bool IsAutoCGetDropRestOfWord + { + get + { + return FastPerform(2271, 0, 0) != 0; + } + set + { + FastPerform(2270, (uint)(value ? 1 : 0), 0); + } + } + + /// + /// Retrieve the auto-completion list type-separator character. + /// + /// + /// Autogenerated: IGEN08 + public int AutoCTypeSeparator + { + get + { + return (int)FastPerform(2285, 0, 0); + } + set + { + FastPerform(2286, (uint)value, 0); + } + } + + /// + /// Retrieve indentation size. + /// + /// + /// Autogenerated: IGEN08 + public int Indent + { + get + { + return (int)FastPerform(2123, 0, 0); + } + set + { + FastPerform(2122, (uint)value, 0); + } + } + + /// + /// Retrieve whether tabs will be used in indentation. + /// + /// + /// Autogenerated: IGEN07 + public bool IsUseTabs + { + get + { + return FastPerform(2125, 0, 0) != 0; + } + set + { + FastPerform(2124, (uint)(value ? 1 : 0), 0); + } + } + + /// + /// Retrieve the number of columns that a line is indented. + /// + /// + /// Autogenerated: IGEN29 + public int GetLineIndentation(int line) + { + return (int)FastPerform(2127, (uint)line, 0); + } + + /// + /// Change the indentation of a line to a number of columns. + /// + /// + /// Autogenerated: IGEN30 + public void SetLineIndentation(int line, int indentSize) + { + FastPerform(2126, (uint)line, (uint)indentSize); + } + + /// + /// Retrieve the position before the first non indentation character on a line. + /// + /// + /// Autogenerated: IGEN18 + public int LineIndentPosition(int line) + { + return (int)FastPerform(2128, (uint)line, 0); + } + + /// + /// Retrieve the column number of a position, taking tab width into account. + /// + /// + /// Autogenerated: IGEN18 + public int Column(int pos) + { + return (int)FastPerform(2129, (uint)pos, 0); + } + + /// + /// Is the horizontal scroll bar visible? + /// + /// + /// Autogenerated: IGEN07 + public bool IsHScrollBar + { + get + { + return FastPerform(2131, 0, 0) != 0; + } + set + { + FastPerform(2130, (uint)(value ? 1 : 0), 0); + } + } + + /// + /// Are the indentation guides visible? + /// + /// + /// Autogenerated: IGEN07 + public int IndentationGuides + { + get + { + return (int)FastPerform(2133, 0, 0); + } + set + { + FastPerform(2132, (uint)value, 0); + } + } + + /// + /// Get the highlighted indentation guide column. + /// + /// + /// Autogenerated: IGEN08 + public int HighlightGuide + { + get + { + return (int)FastPerform(2135, 0, 0); + } + set + { + FastPerform(2134, (uint)value, 0); + } + } + + /// + /// Get the position after the last visible characters on a line. + /// + /// + /// Autogenerated: IGEN18 + public int LineEndPosition(int line) + { + return (int)FastPerform(2136, (uint)line, 0); + } + + /// + /// Get the code page used to interpret the bytes of the document as characters. + /// + /// + /// Autogenerated: IGEN08 + public int CodePage + { + get + { + return (int)FastPerform(2137, 0, 0); + } + set + { + FastPerform(2037, (uint)value, 0); + } + } + + /// + /// Get the foreground colour of the caret. + /// + /// + /// Autogenerated: IGEN08 + public int CaretFore + { + get + { + return (int)FastPerform(2138, 0, 0); + } + set + { + FastPerform(2069, (uint)value, 0); + } + } + + /// + /// In palette mode? + /// + /// + /// Autogenerated: IGEN07 + public bool IsUsePalette + { + get + { + return FastPerform(2139, 0, 0) != 0; + } + set + { + FastPerform(2039, (uint)(value ? 1 : 0), 0); + } + } + + /// + /// In read-only mode? + /// + /// + /// Autogenerated: IGEN07 + public bool IsReadOnly + { + get + { + return FastPerform(2140, 0, 0) != 0; + } + set + { + FastPerform(2171, (uint)(value ? 1 : 0), 0); + } + } + + /// + /// Returns the position at the start of the selection. + /// + /// + /// Autogenerated: IGEN08 + public int SelectionStart + { + get + { + return (int)FastPerform(2143, 0, 0); + } + set + { + FastPerform(2142, (uint)value, 0); + } + } + + /// + /// Returns the position at the end of the selection. + /// + /// + /// Autogenerated: IGEN08 + public int SelectionEnd + { + get + { + return (int)FastPerform(2145, 0, 0); + } + set + { + FastPerform(2144, (uint)value, 0); + } + } + + /// + /// Returns the print magnification. + /// + /// + /// Autogenerated: IGEN08 + public int PrintMagnification + { + get + { + return (int)FastPerform(2147, 0, 0); + } + set + { + FastPerform(2146, (uint)value, 0); + } + } + + /// + /// Returns the print colour mode. + /// + /// + /// Autogenerated: IGEN08 + public int PrintColourMode + { + get + { + return (int)FastPerform(2149, 0, 0); + } + set + { + FastPerform(2148, (uint)value, 0); + } + } + + /// + /// Retrieve the display line at the top of the display. + /// + /// + /// Autogenerated: IGEN06 + public int FirstVisibleLine + { + get + { + return (int)FastPerform(2152, 0, 0); + } + } + + /// + /// Returns the number of lines in the document. There is always at least one. + /// + /// + /// Autogenerated: IGEN06 + public int LineCount + { + get + { + return (int)FastPerform(2154, 0, 0); + } + } + + /// + /// Returns the size in pixels of the left margin. + /// + /// + /// Autogenerated: IGEN08 + public int MarginLeft + { + get + { + return (int)FastPerform(2156, 0, 0); + } + set + { + FastPerform(2155, (uint)value, 0); + } + } + + /// + /// Returns the size in pixels of the right margin. + /// + /// + /// Autogenerated: IGEN08 + public int MarginRight + { + get + { + return (int)FastPerform(2158, 0, 0); + } + set + { + FastPerform(2157, (uint)value, 0); + } + } + + /// + /// Is the document different from when it was last saved? + /// + /// + /// Autogenerated: IGEN05 + public bool IsModify + { + get + { + return FastPerform(2159, 0, 0) != 0; + } + } + + /// + /// Retrieve the number of characters in the document. + /// + /// + /// Autogenerated: IGEN06 + public int TextLength + { + get + { + return (int)FastPerform(2183, 0, 0); + } + } + + /// + /// Retrieve a pointer to a function that processes messages for this Scintilla. + /// + /// + /// Autogenerated: IGEN06 + public int DirectFunction + { + get + { + return (int)FastPerform(2184, 0, 0); + } + } + + /// + /// Retrieve a pointer value to use as the first argument when calling + /// the function returned by GetDirectFunction. + /// + /// + /// Autogenerated: IGEN06 + public int DirectPointer + { + get + { + return (int)FastPerform(2185, 0, 0); + } + } + + /// + /// Returns true if overtype mode is active otherwise false is returned. + /// + /// + /// Autogenerated: IGEN07 + public bool IsOvertype + { + get + { + return FastPerform(2187, 0, 0) != 0; + } + set + { + FastPerform(2186, (uint)(value ? 1 : 0), 0); + } + } + + /// + /// Returns the width of the insert mode caret. + /// + /// + /// Autogenerated: IGEN08 + public int CaretWidth + { + get + { + return (int)FastPerform(2189, 0, 0); + } + set + { + FastPerform(2188, (uint)value, 0); + } + } + + /// + /// Get the position that starts the target. + /// + /// + /// Autogenerated: IGEN08 + public int TargetStart + { + get + { + return (int)FastPerform(2191, 0, 0); + } + set + { + FastPerform(2190, (uint)value, 0); + } + } + + /// + /// Get the position that ends the target. + /// + /// + /// Autogenerated: IGEN08 + public int TargetEnd + { + get + { + return (int)FastPerform(2193, 0, 0); + } + set + { + FastPerform(2192, (uint)value, 0); + } + } + + /// + /// Get the search flags used by SearchInTarget. + /// + /// + /// Autogenerated: IGEN08 + public int SearchFlags + { + get + { + return (int)FastPerform(2199, 0, 0); + } + set + { + FastPerform(2198, (uint)value, 0); + } + } + + /// + /// Retrieve the fold level of a line. + /// + /// + /// Autogenerated: IGEN29 + public int GetFoldLevel(int line) + { + return (int)FastPerform(2223, (uint)line, 0); + } + + /// + /// Set the fold level of a line. + /// This encodes an integer level along with flags indicating whether the + /// line is a header and whether it is effectively white space. + /// + /// + /// Autogenerated: IGEN30 + public void SetFoldLevel(int line, int level) + { + FastPerform(2222, (uint)line, (uint)level); + } + + /// + /// Find the last child line of a header line. + /// + /// + /// Autogenerated: IGEN19 + public int LastChild(int line, int level) + { + return (int)FastPerform(2224, (uint)line, (uint)level); + } + + /// + /// Find the last child line of a header line. + /// + /// + /// Autogenerated: IGEN18 + public int LastChild(int line) + { + return (int)FastPerform(2224, (uint)line, 0); + } + + /// + /// Find the parent line of a child line. + /// + /// + /// Autogenerated: IGEN18 + public int FoldParent(int line) + { + return (int)FastPerform(2225, (uint)line, 0); + } + + /// + /// Is a line visible? + /// + /// + /// Autogenerated: IGEN05 + public bool IsLineVisible + { + get + { + return FastPerform(2228, 0, 0) != 0; + } + } + + /// + /// Is a header line expanded? + /// + /// + /// Autogenerated: IGEN31 + public bool FoldExpanded(int line) + { + return FastPerform(2230, (uint)line, 0) != 0; + } + + /// + /// Show the children of a header line. + /// + /// + /// Autogenerated: IGEN32 + public void FoldExpanded(int line, bool expanded) + { + FastPerform(2229, (uint)line, (uint)(expanded ? 1 : 0)); + } + + /// + /// Does a tab pressed when caret is within indentation indent? + /// + /// + /// Autogenerated: IGEN07 + public bool IsTabIndents + { + get + { + return FastPerform(2261, 0, 0) != 0; + } + set + { + FastPerform(2260, (uint)(value ? 1 : 0), 0); + } + } + + /// + /// Does a backspace pressed when caret is within indentation unindent? + /// + /// + /// Autogenerated: IGEN07 + public bool IsBackSpaceUnIndents + { + get + { + return FastPerform(2263, 0, 0) != 0; + } + set + { + FastPerform(2262, (uint)(value ? 1 : 0), 0); + } + } + + /// + /// Retrieve the time the mouse must sit still to generate a mouse dwell event. + /// + /// + /// Autogenerated: IGEN08 + public int MouseDwellTime + { + get + { + return (int)FastPerform(2265, 0, 0); + } + set + { + FastPerform(2264, (uint)value, 0); + } + } + + /// + /// Retrieve whether text is word wrapped. + /// + /// + /// Autogenerated: IGEN08 + public int WrapMode + { + get + { + return (int)FastPerform(2269, 0, 0); + } + set + { + FastPerform(2268, (uint)value, 0); + } + } + + /// + /// Retrive the display mode of visual flags for wrapped lines. + /// + /// + /// Autogenerated: IGEN08 + public int WrapVisualFlags + { + get + { + return (int)FastPerform(2461, 0, 0); + } + set + { + FastPerform(2460, (uint)value, 0); + } + } + + /// + /// Retrive the location of visual flags for wrapped lines. + /// + /// + /// Autogenerated: IGEN08 + public int WrapVisualFlagsLocation + { + get + { + return (int)FastPerform(2463, 0, 0); + } + set + { + FastPerform(2462, (uint)value, 0); + } + } + + /// + /// Retrive the start indent for wrapped lines. + /// + /// + /// Autogenerated: IGEN08 + public int WrapStartIndent + { + get + { + return (int)FastPerform(2465, 0, 0); + } + set + { + FastPerform(2464, (uint)value, 0); + } + } + + /// + /// Retrieve the degree of caching of layout information. + /// + /// + /// Autogenerated: IGEN08 + public int LayoutCache + { + get + { + return (int)FastPerform(2273, 0, 0); + } + set + { + FastPerform(2272, (uint)value, 0); + } + } + + /// + /// Retrieve the document width assumed for scrolling. + /// + /// + /// Autogenerated: IGEN08 + public int ScrollWidth + { + get + { + return (int)FastPerform(2275, 0, 0); + } + set + { + FastPerform(2274, (uint)value, 0); + } + } + + /// + /// Retrieve whether the maximum scroll position has the last + /// line at the bottom of the view. + /// + /// + /// Autogenerated: IGEN08 + public int EndAtLastLine + { + get + { + return (int)FastPerform(2278, 0, 0); + } + set + { + FastPerform(2277, (uint)value, 0); + } + } + + /// + /// Is the vertical scroll bar visible? + /// + /// + /// Autogenerated: IGEN07 + public bool IsVScrollBar + { + get + { + return FastPerform(2281, 0, 0) != 0; + } + set + { + FastPerform(2280, (uint)(value ? 1 : 0), 0); + } + } + + /// + /// Is drawing done in two phases with backgrounds drawn before faoregrounds? + /// + /// + /// Autogenerated: IGEN07 + public bool IsTwoPhaseDraw + { + get + { + return FastPerform(2283, 0, 0) != 0; + } + set + { + FastPerform(2284, (uint)(value ? 1 : 0), 0); + } + } + + /// + /// Are the end of line characters visible? + /// + /// + /// Autogenerated: IGEN07 + public bool IsViewEOL + { + get + { + return FastPerform(2355, 0, 0) != 0; + } + set + { + FastPerform(2356, (uint)(value ? 1 : 0), 0); + } + } + + /// + /// Retrieve a pointer to the document object. + /// + /// + /// Autogenerated: IGEN08 + public int DocPointer + { + get + { + return (int)FastPerform(2357, 0, 0); + } + set + { + FastPerform(2358, (uint)value, 0); + } + } + + /// + /// Retrieve the column number which text should be kept within. + /// + /// + /// Autogenerated: IGEN08 + public int EdgeColumn + { + get + { + return (int)FastPerform(2360, 0, 0); + } + set + { + FastPerform(2361, (uint)value, 0); + } + } + + /// + /// Retrieve the edge highlight mode. + /// + /// + /// Autogenerated: IGEN08 + public int EdgeMode + { + get + { + return (int)FastPerform(2362, 0, 0); + } + set + { + FastPerform(2363, (uint)value, 0); + } + } + + /// + /// Retrieve the colour used in edge indication. + /// + /// + /// Autogenerated: IGEN08 + public int EdgeColour + { + get + { + return (int)FastPerform(2364, 0, 0); + } + set + { + FastPerform(2365, (uint)value, 0); + } + } + + /// + /// Retrieves the number of lines completely visible. + /// + /// + /// Autogenerated: IGEN06 + public int LinesOnScreen + { + get + { + return (int)FastPerform(2370, 0, 0); + } + } + + /// + /// Is the selection rectangular? The alternative is the more common stream selection. + /// + /// + /// Autogenerated: IGEN05 + public bool IsSelectionIsRectangle + { + get + { + return FastPerform(2372, 0, 0) != 0; + } + } + + /// + /// Set the zoom level. This number of points is added to the size of all fonts. + /// It may be positive to magnify or negative to reduce. + /// Retrieve the zoom level. + /// + /// + /// Autogenerated: IGEN08 + public int ZoomLevel + { + get + { + return (int)FastPerform(2374, 0, 0); + } + set + { + FastPerform(2373, (uint)value, 0); + } + } + + /// + /// Get which document modification events are sent to the container. + /// + /// + /// Autogenerated: IGEN08 + public int ModEventMask + { + get + { + return (int)FastPerform(2378, 0, 0); + } + set + { + FastPerform(2359, (uint)value, 0); + } + } + + /// + /// Change internal focus flag. + /// Get internal focus flag. + /// + /// + /// Autogenerated: IGEN07 + public bool IsFocus + { + get + { + return FastPerform(2381, 0, 0) != 0; + } + set + { + FastPerform(2380, (uint)(value ? 1 : 0), 0); + } + } + + /// + /// Change error status - 0 = OK. + /// Get error status. + /// + /// + /// Autogenerated: IGEN08 + public int Status + { + get + { + return (int)FastPerform(2383, 0, 0); + } + set + { + FastPerform(2382, (uint)value, 0); + } + } + + /// + /// Set whether the mouse is captured when its button is pressed. + /// Get whether mouse gets captured. + /// + /// + /// Autogenerated: IGEN07 + public bool IsMouseDownCaptures + { + get + { + return FastPerform(2385, 0, 0) != 0; + } + set + { + FastPerform(2384, (uint)(value ? 1 : 0), 0); + } + } + + /// + /// Sets the cursor to one of the SC_CURSOR* values. + /// Get cursor type. + /// + /// + /// Autogenerated: IGEN08 + public int CursorType + { + get + { + return (int)FastPerform(2387, 0, 0); + } + set + { + FastPerform(2386, (uint)value, 0); + } + } + + /// + /// Change the way control characters are displayed: + /// If symbol is < 32, keep the drawn way, else, use the given character. + /// Get the way control characters are displayed. + /// + /// + /// Autogenerated: IGEN08 + public int ControlCharSymbol + { + get + { + return (int)FastPerform(2389, 0, 0); + } + set + { + FastPerform(2388, (uint)value, 0); + } + } + + /// + /// Get and Set the xOffset (ie, horizonal scroll position). + /// + /// + /// Autogenerated: IGEN08 + public int XOffset + { + get + { + return (int)FastPerform(2398, 0, 0); + } + set + { + FastPerform(2397, (uint)value, 0); + } + } + + /// + /// Is printing line wrapped? + /// + /// + /// Autogenerated: IGEN08 + public int PrintWrapMode + { + get + { + return (int)FastPerform(2407, 0, 0); + } + set + { + FastPerform(2406, (uint)value, 0); + } + } + + /// + /// Get the mode of the current selection. + /// + /// + /// Autogenerated: IGEN08 + public int SelectionMode + { + get + { + return (int)FastPerform(2423, 0, 0); + } + set + { + FastPerform(2422, (uint)value, 0); + } + } + + /// + /// Retrieve the lexing language of the document. + /// + /// + /// Autogenerated: IGEN08 + public int Lexer + { + get + { + return (int)FastPerform(4002, 0, 0); + } + set + { + FastPerform(4001, (uint)value, 0); + } + } + + /// + /// Clear all the styles and make equivalent to the global default style. + /// + /// + /// Autogenerated: IGEN20 + public void StyleClearAll() + { + FastPerform(2050, 0, 0); + } + + /// + /// Set the foreground colour of a style. + /// + /// + /// Autogenerated: IGEN24 + public void StyleSetFore(int style, int fore) + { + FastPerform(2051, (uint)style, (uint)fore); + } + + /// + /// Set the background colour of a style. + /// + /// + /// Autogenerated: IGEN24 + public void StyleSetBack(int style, int back) + { + FastPerform(2052, (uint)style, (uint)back); + } + + /// + /// Set a style to be bold or not. + /// + /// + /// Autogenerated: IGEN25 + public void StyleSetBold(int style, bool bold) + { + FastPerform(2053, (uint)style, (uint)(bold ? 1 : 0)); + } + + /// + /// Set a style to be italic or not. + /// + /// + /// Autogenerated: IGEN25 + public void StyleSetItalic(int style, bool italic) + { + FastPerform(2054, (uint)style, (uint)(italic ? 1 : 0)); + } + + /// + /// Set the size of characters of a style. + /// + /// + /// Autogenerated: IGEN24 + public void StyleSetSize(int style, int sizePoints) + { + FastPerform(2055, (uint)style, (uint)sizePoints); + } + + /// + /// Set the font of a style. + /// + /// + /// Autogenerated: IGEN27 + unsafe public void StyleSetFont(int style, string fontName) + { + if (fontName == null || fontName.Equals("")) + fontName = "\0\0"; + + fixed (byte* b = System.Text.UTF8Encoding.UTF8.GetBytes(fontName)) + FastPerform(2056, (uint)style, (uint)b); + } + + + /// + /// Set a style to have its end of line filled or not. + /// + /// + /// Autogenerated: IGEN25 + public void StyleSetEOLFilled(int style, bool filled) + { + FastPerform(2057, (uint)style, (uint)(filled ? 1 : 0)); + } + + /// + /// Set a style to be underlined or not. + /// + /// + /// Autogenerated: IGEN25 + public void StyleSetUnderline(int style, bool underline) + { + FastPerform(2059, (uint)style, (uint)(underline ? 1 : 0)); + } + + /// + /// Set a style to be mixed case, or to force upper or lower case. + /// + /// + /// Autogenerated: IGEN24 + public void StyleSetCase(int style, int caseForce) + { + FastPerform(2060, (uint)style, (uint)caseForce); + } + + /// + /// Set the character set of the font in a style. + /// + /// + /// Autogenerated: IGEN24 + public void StyleSetCharacterSet(int style, int characterSet) + { + FastPerform(2066, (uint)style, (uint)characterSet); + } + + /// + /// Set a style to be a hotspot or not. + /// + /// + /// Autogenerated: IGEN25 + public void StyleSetHotSpot(int style, bool hotspot) + { + FastPerform(2409, (uint)style, (uint)(hotspot ? 1 : 0)); + } + + /// + /// Set a style to be visible or not. + /// + /// + /// Autogenerated: IGEN25 + public void StyleSetVisible(int style, bool visible) + { + FastPerform(2074, (uint)style, (uint)(visible ? 1 : 0)); + } + + /// + /// Set the set of characters making up words for when moving or selecting by word. + /// First sets deaults like SetCharsDefault. + /// + /// + /// Autogenerated: IGEN28 + unsafe public void WordChars(string characters) + { + if (characters == null || characters.Equals("")) + characters = "\0\0"; + + fixed (byte* b = System.Text.UTF8Encoding.UTF8.GetBytes(characters)) + FastPerform(2077, 0, (uint)b); + } + + + /// + /// Set a style to be changeable or not (read only). + /// Experimental feature, currently buggy. + /// + /// + /// Autogenerated: IGEN25 + public void StyleSetChangeable(int style, bool changeable) + { + FastPerform(2099, (uint)style, (uint)(changeable ? 1 : 0)); + } + + /// + /// Define a set of characters that when typed will cause the autocompletion to + /// choose the selected item. + /// + /// + /// Autogenerated: IGEN28 + unsafe public void AutoCSetFillUps(string characterSet) + { + if (characterSet == null || characterSet.Equals("")) + characterSet = "\0\0"; + + fixed (byte* b = System.Text.UTF8Encoding.UTF8.GetBytes(characterSet)) + FastPerform(2112, 0, (uint)b); + } + + + /// + /// Set a fore colour for active hotspots. + /// + /// + /// Autogenerated: IGEN26 + public void HotspotActiveFore(bool useSetting, int fore) + { + FastPerform(2410, (uint)(useSetting ? 1 : 0), (uint)fore); + } + + /// + /// Set a back colour for active hotspots. + /// + /// + /// Autogenerated: IGEN26 + public void HotspotActiveBack(bool useSetting, int back) + { + FastPerform(2411, (uint)(useSetting ? 1 : 0), (uint)back); + } + + /// + /// Set the set of characters making up whitespace for when moving or selecting by word. + /// Should be called after SetWordChars. + /// + /// + /// Autogenerated: IGEN28 + unsafe public void WhitespaceChars(string characters) + { + if (characters == null || characters.Equals("")) + characters = "\0\0"; + + fixed (byte* b = System.Text.UTF8Encoding.UTF8.GetBytes(characters)) + FastPerform(2443, 0, (uint)b); + } + + + /// + /// Set up a value that may be used by a lexer for some optional feature. + /// + /// + /// Autogenerated: IGEN27 + unsafe public void Property(string key, string value) + { + if (key == null || key.Equals("")) + key = "\0\0"; + if (value == null || value.Equals("")) + value = "\0\0"; + + fixed (byte* b = System.Text.UTF8Encoding.UTF8.GetBytes(value)) + fixed (byte* b2 = System.Text.UTF8Encoding.UTF8.GetBytes(key)) + FastPerform(4004, (uint)b2, (uint)b); + } + + + /// + /// Set up the key words used by the lexer. + /// + /// + /// Autogenerated: IGEN27 + unsafe public void KeyWords(int keywordSet, string keyWords) + { + if (keyWords == null || keyWords.Equals("")) + keyWords = "\0\0"; + + fixed (byte* b = System.Text.UTF8Encoding.UTF8.GetBytes(keyWords)) + FastPerform(4005, (uint)keywordSet, (uint)b); + } + + + /// + /// Set the lexing language of the document based on string name. + /// + /// + /// Autogenerated: IGEN28 + unsafe public void LexerLanguage(string language) + { + if (language == null || language.Equals("")) + language = "\0\0"; + + fixed (byte* b = System.Text.UTF8Encoding.UTF8.GetBytes(language)) + FastPerform(4006, 0, (uint)b); + } + + #endregion + + #region ================== Contructor / Disposer + + // Constructor + public ScintillaControl() + { + this.BackColor = SystemColors.Window; + + // Setup collections + ignoredkeys = new Dictionary(); + + if (!this.DesignMode) Initialize(); + } + + // Initializer + public void Initialize() + { + // Initialize control + libraryptr = General.LoadLibrary(SCINTILLA_FILENAME); + controlptr = General.CreateWindowEx(0, SCINTILLA_CLASSNAME, "", WS_CHILD | WS_VISIBLE | WS_TABSTOP, 0, 0, + this.Width, this.Height, this.Handle, 0, new IntPtr(0), null); + + // Get a direct pointer + directptr = (int)SlowPerform(2185, 0, 0); + + // Don't know why this is done here again + directptr = (int)FastPerform(2185, 0, 0); + } + + // Disposer + protected override void Dispose(bool disposing) + { + // Disposing? + if (!base.IsDisposed) + { + if (!this.DesignMode) + { + // Dispose managed resources + if (disposing) + { + + + } + + // Clean up + // Why does this crash? + //ClearRegisteredImages(); + + // Dispose unmanaged elements + if (controlptr != IntPtr.Zero) General.DestroyWindow(controlptr); + if (libraryptr != IntPtr.Zero) General.FreeLibrary(libraryptr); + } + } + + base.Dispose(disposing); + } + + #endregion + + #region ================== Events + + // When resized + protected override void OnResize(EventArgs e) + { + base.OnResize(e); + + // Resize control + //General.SetWindowPos(controlptr, 0, base.Location.X, base.Location.Y, base.Width, base.Height, 0); + General.SetWindowPos(controlptr, 0, 0, 0, base.Width, base.Height, 0); + } + + // When a windows message is pre-processed + public override bool PreProcessMessage(ref Message m) + { + switch (m.Msg) + { + case WM_KEYUP: + case WM_KEYDOWN: + // Why do I have to call this for my events work properly? + // I should be able to call base.PreProcessMessage, but that doesn't raise my events! + return base.ProcessKeyEventArgs(ref m); + } + return false; + } + + #endregion + + #region ================== Methods + + // Perform a command (using SendMessage) + protected UInt32 SlowPerform(UInt32 message, UInt32 wParam, UInt32 lParam) + { + if (controlptr != IntPtr.Zero) + { + return (UInt32)General.SendMessage(controlptr, message, (int)wParam, (int)lParam); + } + else + { + return 0; + } + } + + // Immediately perform a command (send directly to control) + protected UInt32 FastPerform(UInt32 message, UInt32 wParam, UInt32 lParam) + { + if (controlptr != IntPtr.Zero) + { + return (UInt32)Perform(directptr, message, (UInt32)wParam, (UInt32)lParam); + } + else + { + return 0; + } + } + + private void AddIgnoredKey(Shortcut shortcutkey) + { + int key = (int)shortcutkey; + this.ignoredkeys.Add(key, key); + } + + public void AddIgnoredKey(System.Windows.Forms.Keys key, System.Windows.Forms.Keys modifier) + { + this.ignoredkeys.Add((int)key + (int)modifier, (int)key + (int)modifier); + } + + private void addShortcuts(Menu m) + { + foreach (MenuItem mi in m.MenuItems) + { + if (mi.Shortcut != Shortcut.None) + AddIgnoredKey(mi.Shortcut); + if (mi.MenuItems.Count > 0) + addShortcuts(mi); + } + } + + protected void AddShortcutsFromForm(Form parentForm) + { + if ((parentForm != null) && (parentForm.Menu != null)) + { + addShortcuts(parentForm.Menu); + } + } + + /// + /// Convert all line endings in the document to one mode. + /// + public void ConvertEOLs(ScriptEndOfLine eolMode) + { + ConvertEOLs((int)eolMode); + } + + /// + /// Set the symbol used for a particular marker number. + /// + public void MarkerDefine(int markerNumber, ScriptMarkerSymbol markerSymbol) + { + MarkerDefine(markerNumber, (int)markerSymbol); + } + + /// + /// Set the character set of the font in a style. + /// + public void StyleSetCharacterSet(int style, ScriptCharacterSet characterSet) + { + StyleSetCharacterSet(style, (int)characterSet); + } + + /// + /// Set a style to be mixed case, or to force upper or lower case. + /// + public void StyleSetCase(int style, ScriptCaseVisible caseForce) + { + StyleSetCase(style, (int)caseForce); + } + + #endregion + + #region ================== Message Pump + + // This handles messages + protected override void WndProc(ref System.Windows.Forms.Message m) + { + // Notify message? + if (m.Msg == WM_NOTIFY) + { + SCNotification scn = (SCNotification)Marshal.PtrToStructure(m.LParam, typeof(SCNotification)); + + if (scn.nmhdr.hwndFrom == controlptr) + { + switch (scn.nmhdr.code) + { + #region "scintilla-event-dispatch" + + case (uint)ScintillaEvents.StyleNeeded: + if (StyleNeeded != null) + StyleNeeded(this, scn.position); + break; + + case (uint)ScintillaEvents.CharAdded: + if (CharAdded != null) + CharAdded(this, scn.ch); + break; + + case (uint)ScintillaEvents.SavePointReached: + if (SavePointReached != null) + SavePointReached(this); + break; + + case (uint)ScintillaEvents.SavePointLeft: + if (SavePointLeft != null) + SavePointLeft(this); + break; + + case (uint)ScintillaEvents.ModifyAttemptRO: + if (ModifyAttemptRO != null) + ModifyAttemptRO(this); + break; + + case (uint)ScintillaEvents.Key: + if (Key != null) + Key(this, scn.ch, scn.modifiers); + break; + + case (uint)ScintillaEvents.DoubleClick: + if (DoubleClick != null) + DoubleClick(this); + break; + + case (uint)ScintillaEvents.UpdateUI: + if (UpdateUI != null) + UpdateUI(this); + break; + + case (uint)ScintillaEvents.MacroRecord: + if (MacroRecord != null) + MacroRecord(this, scn.message, scn.wParam, scn.lParam); + break; + + case (uint)ScintillaEvents.MarginClick: + if (MarginClick != null) + MarginClick(this, scn.modifiers, scn.position, scn.margin); + break; + + case (uint)ScintillaEvents.NeedShown: + if (NeedShown != null) + NeedShown(this, scn.position, scn.length); + break; + + case (uint)ScintillaEvents.Painted: + if (Painted != null) + Painted(this); + break; + + case (uint)ScintillaEvents.UserlistSelection: + if (UserListSelection != null) + UserListSelection(this, scn.listType, System.Runtime.InteropServices.Marshal.PtrToStringAuto(scn.text)); + break; + + case (uint)ScintillaEvents.UriDropped: + if (URIDropped != null) + URIDropped(this, System.Runtime.InteropServices.Marshal.PtrToStringAuto(scn.text)); + break; + + case (uint)ScintillaEvents.DwellStart: + if (DwellStart != null) + DwellStart(this, scn.position); + break; + + case (uint)ScintillaEvents.DwellEnd: + if (DwellEnd != null) + DwellEnd(this, scn.position); + break; + + case (uint)ScintillaEvents.Zoom: + if (Zoom != null) + Zoom(this); + break; + + case (uint)ScintillaEvents.HotspotClick: + if (HotSpotClick != null) + HotSpotClick(this, scn.modifiers, scn.position); + break; + + case (uint)ScintillaEvents.HotspotDoubleClick: + if (HotSpotDoubleClick != null) + HotSpotDoubleClick(this, scn.modifiers, scn.position); + break; + + case (uint)ScintillaEvents.CallTipClick: + if (CallTipClick != null) + CallTipClick(this, scn.position); + break; + #endregion + + case (uint)ScintillaEvents.Modified: + if ((scn.modificationType & (uint)ScriptModificationFlags.InsertText) > 0) + if (TextInserted != null) + TextInserted(this, scn.position, scn.length, scn.linesAdded); + if ((scn.modificationType & (uint)ScriptModificationFlags.DeleteText) > 0) + if (TextDeleted != null) + TextDeleted(this, scn.position, scn.length, scn.linesAdded); + if ((scn.modificationType & (uint)ScriptModificationFlags.ChangeStyle) > 0) + if (StyleChanged != null) + StyleChanged(this, scn.position, scn.length); + if ((scn.modificationType & (uint)ScriptModificationFlags.ChangeFold) > 0) + if (FoldChanged != null) + FoldChanged(this, scn.line, scn.foldLevelNow, scn.foldLevelPrev); + if ((scn.modificationType & (uint)ScriptModificationFlags.User) > 0) + if (UserPerformed != null) + UserPerformed(this); + if ((scn.modificationType & (uint)ScriptModificationFlags.Undo) > 0) + if (UndoPerformed != null) + UndoPerformed(this); + if ((scn.modificationType & (uint)ScriptModificationFlags.Redo) > 0) + if (RedoPerformed != null) + RedoPerformed(this); + if ((scn.modificationType & (uint)ScriptModificationFlags.StepInUndoRedo) > 0) + if (LastStepInUndoRedo != null) + LastStepInUndoRedo(this); + if ((scn.modificationType & (uint)ScriptModificationFlags.ChangeMarker) > 0) + if (MarkerChanged != null) + MarkerChanged(this, scn.line); + if ((scn.modificationType & (uint)ScriptModificationFlags.BeforeInsert) > 0) + if (BeforeInsert != null) + BeforeInsert(this, scn.position, scn.length); + if ((scn.modificationType & (uint)ScriptModificationFlags.BeforeDelete) > 0) + if (BeforeDelete != null) + BeforeDelete(this, scn.position, scn.length); + + if (Modified != null) + Modified(this, scn.position, scn.modificationType, System.Runtime.InteropServices.Marshal.PtrToStringAuto(scn.text), scn.length, scn.linesAdded, scn.line, scn.foldLevelNow, scn.foldLevelPrev); + break; + + } + } + + } + else + base.WndProc(ref m); + } + + #endregion + + #region ================== Scintilla Functions + + /// + /// Add text to the document at current position. + /// + /// + /// Autogenerated: IGEN22 + unsafe public void AddText(int length, string text) + { + if (text == null || text.Equals("")) + text = "\0\0"; + + fixed (byte* b = System.Text.UTF8Encoding.UTF8.GetBytes(text)) + FastPerform(2001, (uint)length, (uint)b); + } + + + /// + /// Insert string at a position. + /// + /// + /// Autogenerated: IGEN22 + unsafe public void InsertText(int pos, string text) + { + if (text == null || text.Equals("")) + text = "\0\0"; + + fixed (byte* b = System.Text.UTF8Encoding.UTF8.GetBytes(text)) + FastPerform(2003, (uint)pos, (uint)b); + } + + + /// + /// Delete all text in the document. + /// + /// + /// Autogenerated: IGEN01 + public void ClearAll() + { + FastPerform(2004, 0, 0); + } + + + /// + /// Set all style bytes to 0, remove all folding information. + /// + /// + /// Autogenerated: IGEN01 + public void ClearDocumentStyle() + { + FastPerform(2005, 0, 0); + } + + + /// + /// Redoes the next action on the undo history. + /// + /// + /// Autogenerated: IGEN01 + public void Redo() + { + FastPerform(2011, 0, 0); + } + + + /// + /// Select all the text in the document. + /// + /// + /// Autogenerated: IGEN01 + public void SelectAll() + { + FastPerform(2013, 0, 0); + } + + + /// + /// Remember the current position in the undo history as the position + /// at which the document was saved. + /// + /// + /// Autogenerated: IGEN01 + public void SetSavePoint() + { + FastPerform(2014, 0, 0); + } + + + /// + /// Retrieve the line number at which a particular marker is located. + /// + /// + /// Autogenerated: IGEN03 + public int MarkerLineFromHandle(int handle) + { + return (int)FastPerform(2017, (uint)handle, 0); + } + + + /// + /// Delete a marker. + /// + /// + /// Autogenerated: IGEN03 + public void MarkerDeleteHandle(int handle) + { + FastPerform(2018, (uint)handle, 0); + } + + + /// + /// Find the position from a point within the window. + /// + /// + /// Autogenerated: IGEN16 + public int PositionFromPoint(int x, int y) + { + return (int)FastPerform(2022, (uint)x, (uint)y); + } + + + /// + /// Find the position from a point within the window but return + /// INVALID_POSITION if not close to text. + /// + /// + /// Autogenerated: IGEN16 + public int PositionFromPointClose(int x, int y) + { + return (int)FastPerform(2023, (uint)x, (uint)y); + } + + + /// + /// Set caret to start of a line and ensure it is visible. + /// + /// + /// Autogenerated: IGEN03 + public void GotoLine(int line) + { + FastPerform(2024, (uint)line, 0); + } + + + /// + /// Set caret to a position and ensure it is visible. + /// + /// + /// Autogenerated: IGEN03 + public void GotoPos(int pos) + { + FastPerform(2025, (uint)pos, 0); + } + + + /// + /// Retrieve the text of the line containing the caret. + /// Returns the index of the caret on the line. + /// + /// + /// Autogenerated: IGEN14 + unsafe public string GetCurLine(int length) + { + int sz = (int)FastPerform(2027, (uint)length, 0); + + byte[] buffer = new byte[sz + 1]; + fixed (byte* b = buffer) + FastPerform(2027, (uint)length + 1, (uint)b); + return System.Text.UTF8Encoding.UTF8.GetString(buffer, 0, sz); + } + + /// + /// Length Method for : Retrieve the text of the line containing the caret. + /// Returns the index of the caret on the line. + /// + /// + /// Autogenerated: IGEN15 + public int CurLineSize + { + get + { + return (int)FastPerform(2027, 0, 0); + } + } + + /// + /// Convert all line endings in the document to one mode. + /// + /// + /// Autogenerated: IGEN03 + public void ConvertEOLs(int eolMode) + { + FastPerform(2029, (uint)eolMode, 0); + } + + + /// + /// Set the current styling position to pos and the styling mask to mask. + /// The styling mask can be used to protect some bits in each styling byte from modification. + /// + /// + /// Autogenerated: IGEN16 + public void StartStyling(int pos, int mask) + { + FastPerform(2032, (uint)pos, (uint)mask); + } + + + /// + /// Change style from current styling position for length characters to a style + /// and move the current styling position to after this newly styled segment. + /// + /// + /// Autogenerated: IGEN16 + public void SetStyling(int length, int style) + { + FastPerform(2033, (uint)length, (uint)style); + } + + + /// + /// Set the symbol used for a particular marker number. + /// + /// + /// Autogenerated: IGEN16 + public void MarkerDefine(int markerNumber, int markerSymbol) + { + FastPerform(2040, (uint)markerNumber, (uint)markerSymbol); + } + + + /// + /// Set the foreground colour used for a particular marker number. + /// + /// + /// Autogenerated: IGEN16 + public void MarkerSetFore(int markerNumber, int fore) + { + FastPerform(2041, (uint)markerNumber, (uint)fore); + } + + + /// + /// Set the background colour used for a particular marker number. + /// + /// + /// Autogenerated: IGEN16 + public void MarkerSetBack(int markerNumber, int back) + { + FastPerform(2042, (uint)markerNumber, (uint)back); + } + + + /// + /// Add a marker to a line, returning an ID which can be used to find or delete the marker. + /// + /// + /// Autogenerated: IGEN16 + public int MarkerAdd(int line, int markerNumber) + { + return (int)FastPerform(2043, (uint)line, (uint)markerNumber); + } + + + /// + /// Delete a marker from a line. + /// + /// + /// Autogenerated: IGEN16 + public void MarkerDelete(int line, int markerNumber) + { + FastPerform(2044, (uint)line, (uint)markerNumber); + } + + + /// + /// Delete all markers with a particular number from all lines. + /// + /// + /// Autogenerated: IGEN03 + public void MarkerDeleteAll(int markerNumber) + { + FastPerform(2045, (uint)markerNumber, 0); + } + + + /// + /// Get a bit mask of all the markers set on a line. + /// + /// + /// Autogenerated: IGEN03 + public int MarkerGet(int line) + { + return (int)FastPerform(2046, (uint)line, 0); + } + + + /// + /// Find the next line after lineStart that includes a marker in mask. + /// + /// + /// Autogenerated: IGEN16 + public int MarkerNext(int lineStart, int markerMask) + { + return (int)FastPerform(2047, (uint)lineStart, (uint)markerMask); + } + + + /// + /// Find the previous line before lineStart that includes a marker in mask. + /// + /// + /// Autogenerated: IGEN16 + public int MarkerPrevious(int lineStart, int markerMask) + { + return (int)FastPerform(2048, (uint)lineStart, (uint)markerMask); + } + + + /// + /// Define a marker from a pixmap. + /// + /// + /// Autogenerated: IGEN22 + unsafe public void MarkerDefinePixmap(int markerNumber, string pixmap) + { + if (pixmap == null || pixmap.Equals("")) + pixmap = "\0\0"; + + fixed (byte* b = System.Text.UTF8Encoding.UTF8.GetBytes(pixmap)) + FastPerform(2049, (uint)markerNumber, (uint)b); + } + + + /// + /// Reset the default style to its state at startup + /// + /// + /// Autogenerated: IGEN01 + public void StyleResetDefault() + { + FastPerform(2058, 0, 0); + } + + + /// + /// Set the foreground colour of the selection and whether to use this setting. + /// + /// + /// Autogenerated: IGEN21 + public void SetSelFore(bool useSetting, int fore) + { + FastPerform(2067, (uint)(useSetting ? 1 : 0), (uint)fore); + } + + + /// + /// Set the background colour of the selection and whether to use this setting. + /// + /// + /// Autogenerated: IGEN21 + public void SetSelBack(bool useSetting, int back) + { + FastPerform(2068, (uint)(useSetting ? 1 : 0), (uint)back); + } + + + /// + /// When key+modifier combination km is pressed perform msg. + /// + /// + /// Autogenerated: IGEN16 + public void AssignCmdKey(int km, int msg) + { + FastPerform(2070, (uint)km, (uint)msg); + } + + + /// + /// When key+modifier combination km is pressed do nothing. + /// + /// + /// Autogenerated: IGEN03 + public void ClearCmdKey(int km) + { + FastPerform(2071, (uint)km, 0); + } + + + /// + /// Drop all key mappings. + /// + /// + /// Autogenerated: IGEN01 + public void ClearAllCmdKeys() + { + FastPerform(2072, 0, 0); + } + + + /// + /// Set the styles for a segment of the document. + /// + /// + /// Autogenerated: IGEN22 + unsafe public void SetStylingEx(int length, string styles) + { + if (styles == null || styles.Equals("")) + styles = "\0\0"; + + fixed (byte* b = System.Text.UTF8Encoding.UTF8.GetBytes(styles)) + FastPerform(2073, (uint)length, (uint)b); + } + + + /// + /// Start a sequence of actions that is undone and redone as a unit. + /// May be nested. + /// + /// + /// Autogenerated: IGEN01 + public void BeginUndoAction() + { + FastPerform(2078, 0, 0); + } + + + /// + /// End a sequence of actions that is undone and redone as a unit. + /// + /// + /// Autogenerated: IGEN01 + public void EndUndoAction() + { + FastPerform(2079, 0, 0); + } + + + /// + /// Set the foreground colour of all whitespace and whether to use this setting. + /// + /// + /// Autogenerated: IGEN21 + public void SetWhitespaceFore(bool useSetting, int fore) + { + FastPerform(2084, (uint)(useSetting ? 1 : 0), (uint)fore); + } + + + /// + /// Set the background colour of all whitespace and whether to use this setting. + /// + /// + /// Autogenerated: IGEN21 + public void SetWhitespaceBack(bool useSetting, int back) + { + FastPerform(2085, (uint)(useSetting ? 1 : 0), (uint)back); + } + + + /// + /// Display a auto-completion list. + /// The lenEntered parameter indicates how many characters before + /// the caret should be used to provide context. + /// + /// + /// Autogenerated: IGEN22 + unsafe public void AutoCShow(int lenEntered, string itemList) + { + if (itemList == null || itemList.Equals("")) + itemList = "\0\0"; + + fixed (byte* b = System.Text.UTF8Encoding.UTF8.GetBytes(itemList)) + FastPerform(2100, (uint)lenEntered, (uint)b); + } + + + /// + /// Remove the auto-completion list from the screen. + /// + /// + /// Autogenerated: IGEN01 + public void AutoCCancel() + { + FastPerform(2101, 0, 0); + } + + + /// + /// User has selected an item so remove the list and insert the selection. + /// + /// + /// Autogenerated: IGEN01 + public void AutoCComplete() + { + FastPerform(2104, 0, 0); + } + + + /// + /// Define a set of character that when typed cancel the auto-completion list. + /// + /// + /// Autogenerated: IGEN04 + unsafe public void AutoCStops(string characterSet) + { + if (characterSet == null || characterSet.Equals("")) + characterSet = "\0\0"; + + fixed (byte* b = System.Text.UTF8Encoding.UTF8.GetBytes(characterSet)) + FastPerform(2105, 0, (uint)b); + } + + + /// + /// Select the item in the auto-completion list that starts with a string. + /// + /// + /// Autogenerated: IGEN04 + unsafe public void AutoCSelect(string text) + { + if (text == null || text.Equals("")) + text = "\0\0"; + + fixed (byte* b = System.Text.UTF8Encoding.UTF8.GetBytes(text)) + FastPerform(2108, 0, (uint)b); + } + + + /// + /// Display a list of strings and send notification when user chooses one. + /// + /// + /// Autogenerated: IGEN22 + unsafe public void UserListShow(int listType, string itemList) + { + if (itemList == null || itemList.Equals("")) + itemList = "\0\0"; + + fixed (byte* b = System.Text.UTF8Encoding.UTF8.GetBytes(itemList)) + FastPerform(2117, (uint)listType, (uint)b); + } + + + /// + /// Register an XPM image for use in autocompletion lists. + /// + /// + /// Autogenerated: IGEN22 + unsafe public void RegisterImage(int type, string xpmData) + { + if (xpmData == null || xpmData.Equals("")) + xpmData = "\0\0"; + + fixed (byte* b = System.Text.UTF8Encoding.UTF8.GetBytes(xpmData)) + FastPerform(2405, (uint)type, (uint)b); + } + + + /// + /// Clear all the registered XPM images. + /// + /// + /// Autogenerated: IGEN01 + public void ClearRegisteredImages() + { + FastPerform(2408, 0, 0); + } + + + /// + /// Retrieve the contents of a line. + /// Returns the length of the line. + /// + /// + /// Autogenerated: IGEN14 + unsafe public string GetLine(int line) + { + int sz = (int)FastPerform(2153, (uint)line, 0); + + byte[] buffer = new byte[sz + 1]; + fixed (byte* b = buffer) + FastPerform(2153, (uint)line + 1, (uint)b); + return System.Text.UTF8Encoding.UTF8.GetString(buffer, 0, sz); + } + + /// + /// Length Method for : Retrieve the contents of a line. + /// Returns the length of the line. + /// + /// + /// Autogenerated: IGEN15 + public int LineSize + { + get + { + return (int)FastPerform(2153, 0, 0); + } + } + + /// + /// Select a range of text. + /// + /// + /// Autogenerated: IGEN16 + public void SetSel(int start, int end) + { + FastPerform(2160, (uint)start, (uint)end); + } + + + /// + /// Retrieve the selected text. + /// Return the length of the text. + /// + /// + /// Autogenerated: IGEN12 + unsafe public string SelText + { + get + { + int sz = (int)FastPerform(2161, 0, 0); + + byte[] buffer = new byte[sz + 1]; + fixed (byte* b = buffer) + FastPerform(2161, (UInt32)sz + 1, (uint)b); + return System.Text.UTF8Encoding.UTF8.GetString(buffer, 0, sz); + } + } + + /// + /// Length Method for : Retrieve the selected text. + /// Return the length of the text. + /// + /// + /// Autogenerated: IGEN13 + public int SelTextSize + { + get + { + return (int)FastPerform(2161, 0, 0); + } + } + + /// + /// Draw the selection in normal style or with selection highlighted. + /// + /// + /// Autogenerated: IGEN02 + public void HideSelection(bool normal) + { + FastPerform(2163, (uint)(normal ? 1 : 0), 0); + } + + + /// + /// Retrieve the x value of the point in the window where a position is displayed. + /// + /// + /// Autogenerated: IGEN11 + public int PointXFromPosition(int pos) + { + return (int)FastPerform(2164, 0, (uint)pos); + } + + + /// + /// Retrieve the y value of the point in the window where a position is displayed. + /// + /// + /// Autogenerated: IGEN11 + public int PointYFromPosition(int pos) + { + return (int)FastPerform(2165, 0, (uint)pos); + } + + + /// + /// Retrieve the line containing a position. + /// + /// + /// Autogenerated: IGEN03 + public int LineFromPosition(int pos) + { + return (int)FastPerform(2166, (uint)pos, 0); + } + + + /// + /// Retrieve the position at the start of a line. + /// + /// + /// Autogenerated: IGEN03 + public int PositionFromLine(int line) + { + return (int)FastPerform(2167, (uint)line, 0); + } + + + /// + /// Scroll horizontally and vertically. + /// + /// + /// Autogenerated: IGEN16 + public void LineScroll(int columns, int lines) + { + FastPerform(2168, (uint)columns, (uint)lines); + } + + + /// + /// Ensure the caret is visible. + /// + /// + /// Autogenerated: IGEN01 + public void ScrollCaret() + { + FastPerform(2169, 0, 0); + } + + + /// + /// Replace the selected text with the argument text. + /// + /// + /// Autogenerated: IGEN04 + unsafe public void ReplaceSel(string text) + { + if (text == null || text.Equals("")) + text = "\0\0"; + + fixed (byte* b = System.Text.UTF8Encoding.UTF8.GetBytes(text)) + FastPerform(2170, 0, (uint)b); + } + + + /// + /// Null operation. + /// + /// + /// Autogenerated: IGEN01 + public void Null() + { + FastPerform(2172, 0, 0); + } + + + /// + /// Delete the undo history. + /// + /// + /// Autogenerated: IGEN01 + public void EmptyUndoBuffer() + { + FastPerform(2175, 0, 0); + } + + + /// + /// Undo one action in the undo history. + /// + /// + /// Autogenerated: IGEN01 + public void Undo() + { + FastPerform(2176, 0, 0); + } + + + /// + /// Cut the selection to the clipboard. + /// + /// + /// Autogenerated: IGEN01 + public void Cut() + { + FastPerform(2177, 0, 0); + } + + + /// + /// Copy the selection to the clipboard. + /// + /// + /// Autogenerated: IGEN01 + public void Copy() + { + FastPerform(2178, 0, 0); + } + + + /// + /// Paste the contents of the clipboard into the document replacing the selection. + /// + /// + /// Autogenerated: IGEN01 + public void Paste() + { + FastPerform(2179, 0, 0); + } + + + /// + /// Clear the selection. + /// + /// + /// Autogenerated: IGEN01 + public void Clear() + { + FastPerform(2180, 0, 0); + } + + + /// + /// Replace the contents of the document with the argument text. + /// + /// + /// Autogenerated: IGEN04 + unsafe public void SetText(byte[] text) + { + if (text.Length == 0) + text = new byte[2] { 0, 0 }; + + fixed (byte* b = text) + FastPerform(2181, 0, (uint)b); + } + + + /// + /// Retrieve all the text in the document. + /// Returns number of characters retrieved. + /// + /// + /// Autogenerated: IGEN14 + unsafe public byte[] GetText(int length) + { + int sz = (int)FastPerform(2182, (uint)length, 0); + if (sz > 0) + { + byte[] buffer = new byte[sz + 1]; + fixed (byte* b = buffer) + FastPerform(2182, (uint)length + 1, (uint)b); + return buffer; + } + else + { + return new byte[0]; + } + } + + /// + /// Length Method for : Retrieve all the text in the document. + /// Returns number of characters retrieved. + /// + /// + /// Autogenerated: IGEN15 + public int TextSize + { + get + { + return (int)FastPerform(2182, 0, 0); + } + } + + /// + /// Replace the target text with the argument text. + /// Text is counted so it can contain NULs. + /// Returns the length of the replacement text. + /// + /// + /// Autogenerated: IGEN22 + unsafe public int ReplaceTarget(int length, string text) + { + if (text == null || text.Equals("")) + text = "\0\0"; + + fixed (byte* b = System.Text.UTF8Encoding.UTF8.GetBytes(text)) + return (int)FastPerform(2194, (uint)length, (uint)b); + } + + + /// + /// Replace the target text with the argument text after \d processing. + /// Text is counted so it can contain NULs. + /// Looks for \d where d is between 1 and 9 and replaces these with the strings + /// matched in the last search operation which were surrounded by \( and \). + /// Returns the length of the replacement text including any change + /// caused by processing the \d patterns. + /// + /// + /// Autogenerated: IGEN22 + unsafe public int ReplaceTargetRE(int length, string text) + { + if (text == null || text.Equals("")) + text = "\0\0"; + + fixed (byte* b = System.Text.UTF8Encoding.UTF8.GetBytes(text)) + return (int)FastPerform(2195, (uint)length, (uint)b); + } + + + /// + /// Search for a counted string in the target and set the target to the found + /// range. Text is counted so it can contain NULs. + /// Returns length of range or -1 for failure in which case target is not moved. + /// + /// + /// Autogenerated: IGEN22 + unsafe public int SearchInTarget(int length, string text) + { + if (text == null || text.Equals("")) + text = "\0\0"; + + fixed (byte* b = System.Text.UTF8Encoding.UTF8.GetBytes(text)) + return (int)FastPerform(2197, (uint)length, (uint)b); + } + + + /// + /// Show a call tip containing a definition near position pos. + /// + /// + /// Autogenerated: IGEN22 + unsafe public void CallTipShow(int pos, string definition) + { + if (definition == null || definition.Equals("")) + definition = "\0\0"; + + fixed (byte* b = System.Text.UTF8Encoding.UTF8.GetBytes(definition)) + FastPerform(2200, (uint)pos, (uint)b); + } + + + /// + /// Remove the call tip from the screen. + /// + /// + /// Autogenerated: IGEN01 + public void CallTipCancel() + { + FastPerform(2201, 0, 0); + } + + + /// + /// Highlight a segment of the definition. + /// + /// + /// Autogenerated: IGEN16 + public void CallTipSetHlt(int start, int end) + { + FastPerform(2204, (uint)start, (uint)end); + } + + + /// + /// Find the display line of a document line taking hidden lines into account. + /// + /// + /// Autogenerated: IGEN03 + public int VisibleFromDocLine(int line) + { + return (int)FastPerform(2220, (uint)line, 0); + } + + + /// + /// Find the document line of a display line taking hidden lines into account. + /// + /// + /// Autogenerated: IGEN03 + public int DocLineFromVisible(int lineDisplay) + { + return (int)FastPerform(2221, (uint)lineDisplay, 0); + } + + + /// + /// Make a range of lines visible. + /// + /// + /// Autogenerated: IGEN16 + public void ShowLines(int lineStart, int lineEnd) + { + FastPerform(2226, (uint)lineStart, (uint)lineEnd); + } + + + /// + /// Make a range of lines invisible. + /// + /// + /// Autogenerated: IGEN16 + public void HideLines(int lineStart, int lineEnd) + { + FastPerform(2227, (uint)lineStart, (uint)lineEnd); + } + + + /// + /// Switch a header line between expanded and contracted. + /// + /// + /// Autogenerated: IGEN03 + public void ToggleFold(int line) + { + FastPerform(2231, (uint)line, 0); + } + + + /// + /// Ensure a particular line is visible by expanding any header line hiding it. + /// + /// + /// Autogenerated: IGEN03 + public void EnsureVisible(int line) + { + FastPerform(2232, (uint)line, 0); + } + + + /// + /// Set some style options for folding. + /// + /// + /// Autogenerated: IGEN03 + public void SetFoldFlags(int flags) + { + FastPerform(2233, (uint)flags, 0); + } + + + /// + /// Ensure a particular line is visible by expanding any header line hiding it. + /// Use the currently set visibility policy to determine which range to display. + /// + /// + /// Autogenerated: IGEN03 + public void EnsureVisibleEnforcePolicy(int line) + { + FastPerform(2234, (uint)line, 0); + } + + + /// + /// Get position of start of word. + /// + /// + /// Autogenerated: IGEN23 + public int WordStartPosition(int pos, bool onlyWordCharacters) + { + return (int)FastPerform(2266, (uint)pos, (uint)(onlyWordCharacters ? 1 : 0)); + } + + + /// + /// Get position of end of word. + /// + /// + /// Autogenerated: IGEN23 + public int WordEndPosition(int pos, bool onlyWordCharacters) + { + return (int)FastPerform(2267, (uint)pos, (uint)(onlyWordCharacters ? 1 : 0)); + } + + + /// + /// Measure the pixel width of some text in a particular style. + /// NUL terminated text argument. + /// Does not handle tab or control characters. + /// + /// + /// Autogenerated: IGEN22 + unsafe public int TextWidth(int style, string text) + { + if (text == null || text.Equals("")) + text = "\0\0"; + + fixed (byte* b = System.Text.UTF8Encoding.UTF8.GetBytes(text)) + return (int)FastPerform(2276, (uint)style, (uint)b); + } + + + /// + /// Retrieve the height of a particular line of text in pixels. + /// + /// + /// Autogenerated: IGEN03 + public int TextHeight(int line) + { + return (int)FastPerform(2279, (uint)line, 0); + } + + + /// + /// Append a string to the end of the document without changing the selection. + /// + /// + /// Autogenerated: IGEN22 + unsafe public void AppendText(int length, string text) + { + if (text == null || text.Equals("")) + text = "\0\0"; + + fixed (byte* b = System.Text.UTF8Encoding.UTF8.GetBytes(text)) + FastPerform(2282, (uint)length, (uint)b); + } + + + /// + /// Make the target range start and end be the same as the selection range start and end. + /// + /// + /// Autogenerated: IGEN01 + public void TargetFromSelection() + { + FastPerform(2287, 0, 0); + } + + + /// + /// Join the lines in the target. + /// + /// + /// Autogenerated: IGEN01 + public void LinesJoin() + { + FastPerform(2288, 0, 0); + } + + + /// + /// Split the lines in the target into lines that are less wide than pixelWidth + /// where possible. + /// + /// + /// Autogenerated: IGEN03 + public void LinesSplit(int pixelWidth) + { + FastPerform(2289, (uint)pixelWidth, 0); + } + + + /// + /// Set the colours used as a chequerboard pattern in the fold margin + /// + /// + /// Autogenerated: IGEN21 + public void SetFoldMarginColour(bool useSetting, int back) + { + FastPerform(2290, (uint)(useSetting ? 1 : 0), (uint)back); + } + + + /// + /// Set the colours used as a chequerboard pattern in the fold margin + /// + /// + /// Autogenerated: IGEN21 + public void SetFoldMarginHiColour(bool useSetting, int fore) + { + FastPerform(2291, (uint)(useSetting ? 1 : 0), (uint)fore); + } + + + /// + /// Move caret down one line. + /// + /// + /// Autogenerated: IGEN01 + public void LineDown() + { + FastPerform(2300, 0, 0); + } + + + /// + /// Move caret down one line extending selection to new caret position. + /// + /// + /// Autogenerated: IGEN01 + public void LineDownExtend() + { + FastPerform(2301, 0, 0); + } + + + /// + /// Move caret up one line. + /// + /// + /// Autogenerated: IGEN01 + public void LineUp() + { + FastPerform(2302, 0, 0); + } + + + /// + /// Move caret up one line extending selection to new caret position. + /// + /// + /// Autogenerated: IGEN01 + public void LineUpExtend() + { + FastPerform(2303, 0, 0); + } + + + /// + /// Move caret left one character. + /// + /// + /// Autogenerated: IGEN01 + public void CharLeft() + { + FastPerform(2304, 0, 0); + } + + + /// + /// Move caret left one character extending selection to new caret position. + /// + /// + /// Autogenerated: IGEN01 + public void CharLeftExtend() + { + FastPerform(2305, 0, 0); + } + + + /// + /// Move caret right one character. + /// + /// + /// Autogenerated: IGEN01 + public void CharRight() + { + FastPerform(2306, 0, 0); + } + + + /// + /// Move caret right one character extending selection to new caret position. + /// + /// + /// Autogenerated: IGEN01 + public void CharRightExtend() + { + FastPerform(2307, 0, 0); + } + + + /// + /// Move caret left one word. + /// + /// + /// Autogenerated: IGEN01 + public void WordLeft() + { + FastPerform(2308, 0, 0); + } + + + /// + /// Move caret left one word extending selection to new caret position. + /// + /// + /// Autogenerated: IGEN01 + public void WordLeftExtend() + { + FastPerform(2309, 0, 0); + } + + + /// + /// Move caret right one word. + /// + /// + /// Autogenerated: IGEN01 + public void WordRight() + { + FastPerform(2310, 0, 0); + } + + + /// + /// Move caret right one word extending selection to new caret position. + /// + /// + /// Autogenerated: IGEN01 + public void WordRightExtend() + { + FastPerform(2311, 0, 0); + } + + + /// + /// Move caret to first position on line. + /// + /// + /// Autogenerated: IGEN01 + public void Home() + { + FastPerform(2312, 0, 0); + } + + + /// + /// Move caret to first position on line extending selection to new caret position. + /// + /// + /// Autogenerated: IGEN01 + public void HomeExtend() + { + FastPerform(2313, 0, 0); + } + + + /// + /// Move caret to last position on line. + /// + /// + /// Autogenerated: IGEN01 + public void LineEnd() + { + FastPerform(2314, 0, 0); + } + + + /// + /// Move caret to last position on line extending selection to new caret position. + /// + /// + /// Autogenerated: IGEN01 + public void LineEndExtend() + { + FastPerform(2315, 0, 0); + } + + + /// + /// Move caret to first position in document. + /// + /// + /// Autogenerated: IGEN01 + public void DocumentStart() + { + FastPerform(2316, 0, 0); + } + + + /// + /// Move caret to first position in document extending selection to new caret position. + /// + /// + /// Autogenerated: IGEN01 + public void DocumentStartExtend() + { + FastPerform(2317, 0, 0); + } + + + /// + /// Move caret to last position in document. + /// + /// + /// Autogenerated: IGEN01 + public void DocumentEnd() + { + FastPerform(2318, 0, 0); + } + + + /// + /// Move caret to last position in document extending selection to new caret position. + /// + /// + /// Autogenerated: IGEN01 + public void DocumentEndExtend() + { + FastPerform(2319, 0, 0); + } + + + /// + /// Move caret one page up. + /// + /// + /// Autogenerated: IGEN01 + public void PageUp() + { + FastPerform(2320, 0, 0); + } + + + /// + /// Move caret one page up extending selection to new caret position. + /// + /// + /// Autogenerated: IGEN01 + public void PageUpExtend() + { + FastPerform(2321, 0, 0); + } + + + /// + /// Move caret one page down. + /// + /// + /// Autogenerated: IGEN01 + public void PageDown() + { + FastPerform(2322, 0, 0); + } + + + /// + /// Move caret one page down extending selection to new caret position. + /// + /// + /// Autogenerated: IGEN01 + public void PageDownExtend() + { + FastPerform(2323, 0, 0); + } + + + /// + /// Switch from insert to overtype mode or the reverse. + /// + /// + /// Autogenerated: IGEN01 + public void EditToggleOvertype() + { + FastPerform(2324, 0, 0); + } + + + /// + /// Cancel any modes such as call tip or auto-completion list display. + /// + /// + /// Autogenerated: IGEN01 + public void Cancel() + { + FastPerform(2325, 0, 0); + } + + + /// + /// Delete the selection or if no selection, the character before the caret. + /// + /// + /// Autogenerated: IGEN01 + public void DeleteBack() + { + FastPerform(2326, 0, 0); + } + + + /// + /// If selection is empty or all on one line replace the selection with a tab character. + /// If more than one line selected, indent the lines. + /// + /// + /// Autogenerated: IGEN01 + public void Tab() + { + FastPerform(2327, 0, 0); + } + + + /// + /// Dedent the selected lines. + /// + /// + /// Autogenerated: IGEN01 + public void BackTab() + { + FastPerform(2328, 0, 0); + } + + + /// + /// Insert a new line, may use a CRLF, CR or LF depending on EOL mode. + /// + /// + /// Autogenerated: IGEN01 + public void NewLine() + { + FastPerform(2329, 0, 0); + } + + + /// + /// Insert a Form Feed character. + /// + /// + /// Autogenerated: IGEN01 + public void FormFeed() + { + FastPerform(2330, 0, 0); + } + + + /// + /// Move caret to before first visible character on line. + /// If already there move to first character on line. + /// + /// + /// Autogenerated: IGEN01 + public void VCHome() + { + FastPerform(2331, 0, 0); + } + + + /// + /// Like VCHome but extending selection to new caret position. + /// + /// + /// Autogenerated: IGEN01 + public void VCHomeExtend() + { + FastPerform(2332, 0, 0); + } + + + /// + /// Magnify the displayed text by increasing the sizes by 1 point. + /// + /// + /// Autogenerated: IGEN01 + public void ZoomIn() + { + FastPerform(2333, 0, 0); + } + + + /// + /// Make the displayed text smaller by decreasing the sizes by 1 point. + /// + /// + /// Autogenerated: IGEN01 + public void ZoomOut() + { + FastPerform(2334, 0, 0); + } + + + /// + /// Delete the word to the left of the caret. + /// + /// + /// Autogenerated: IGEN01 + public void DelWordLeft() + { + FastPerform(2335, 0, 0); + } + + + /// + /// Delete the word to the right of the caret. + /// + /// + /// Autogenerated: IGEN01 + public void DelWordRight() + { + FastPerform(2336, 0, 0); + } + + + /// + /// Cut the line containing the caret. + /// + /// + /// Autogenerated: IGEN01 + public void LineCut() + { + FastPerform(2337, 0, 0); + } + + + /// + /// Delete the line containing the caret. + /// + /// + /// Autogenerated: IGEN01 + public void LineDelete() + { + FastPerform(2338, 0, 0); + } + + + /// + /// Switch the current line with the previous. + /// + /// + /// Autogenerated: IGEN01 + public void LineTranspose() + { + FastPerform(2339, 0, 0); + } + + + /// + /// Duplicate the current line. + /// + /// + /// Autogenerated: IGEN01 + public void LineDuplicate() + { + FastPerform(2404, 0, 0); + } + + + /// + /// Transform the selection to lower case. + /// + /// + /// Autogenerated: IGEN01 + public void LowerCase() + { + FastPerform(2340, 0, 0); + } + + + /// + /// Transform the selection to upper case. + /// + /// + /// Autogenerated: IGEN01 + public void UpperCase() + { + FastPerform(2341, 0, 0); + } + + + /// + /// Scroll the document down, keeping the caret visible. + /// + /// + /// Autogenerated: IGEN01 + public void LineScrollDown() + { + FastPerform(2342, 0, 0); + } + + + /// + /// Scroll the document up, keeping the caret visible. + /// + /// + /// Autogenerated: IGEN01 + public void LineScrollUp() + { + FastPerform(2343, 0, 0); + } + + + /// + /// Delete the selection or if no selection, the character before the caret. + /// Will not delete the character before at the start of a line. + /// + /// + /// Autogenerated: IGEN01 + public void DeleteBackNotLine() + { + FastPerform(2344, 0, 0); + } + + + /// + /// Move caret to first position on display line. + /// + /// + /// Autogenerated: IGEN01 + public void HomeDisplay() + { + FastPerform(2345, 0, 0); + } + + + /// + /// Move caret to first position on display line extending selection to + /// new caret position. + /// + /// + /// Autogenerated: IGEN01 + public void HomeDisplayExtend() + { + FastPerform(2346, 0, 0); + } + + + /// + /// Move caret to last position on display line. + /// + /// + /// Autogenerated: IGEN01 + public void LineEndDisplay() + { + FastPerform(2347, 0, 0); + } + + + /// + /// Move caret to last position on display line extending selection to new + /// caret position. + /// + /// + /// Autogenerated: IGEN01 + public void LineEndDisplayExtend() + { + FastPerform(2348, 0, 0); + } + + + /// + /// + /// + /// Autogenerated: IGEN01 + public void HomeWrap() + { + FastPerform(2349, 0, 0); + } + + + /// + /// + /// + /// Autogenerated: IGEN01 + public void HomeWrapExtend() + { + FastPerform(2450, 0, 0); + } + + + /// + /// + /// + /// Autogenerated: IGEN01 + public void LineEndWrap() + { + FastPerform(2451, 0, 0); + } + + + /// + /// + /// + /// Autogenerated: IGEN01 + public void LineEndWrapExtend() + { + FastPerform(2452, 0, 0); + } + + + /// + /// + /// + /// Autogenerated: IGEN01 + public void VCHomeWrap() + { + FastPerform(2453, 0, 0); + } + + + /// + /// + /// + /// Autogenerated: IGEN01 + public void VCHomeWrapExtend() + { + FastPerform(2454, 0, 0); + } + + + /// + /// Copy the line containing the caret. + /// + /// + /// Autogenerated: IGEN01 + public void LineCopy() + { + FastPerform(2455, 0, 0); + } + + + /// + /// Move the caret inside current view if it's not there already. + /// + /// + /// Autogenerated: IGEN01 + public void MoveCaretInsideView() + { + FastPerform(2401, 0, 0); + } + + + /// + /// How many characters are on a line, not including end of line characters? + /// + /// + /// Autogenerated: IGEN03 + public int LineLength(int line) + { + return (int)FastPerform(2350, (uint)line, 0); + } + + + /// + /// Highlight the characters at two positions. + /// + /// + /// Autogenerated: IGEN16 + public void BraceHighlight(int pos1, int pos2) + { + FastPerform(2351, (uint)pos1, (uint)pos2); + } + + + /// + /// Highlight the character at a position indicating there is no matching brace. + /// + /// + /// Autogenerated: IGEN03 + public void BraceBadLight(int pos) + { + FastPerform(2352, (uint)pos, 0); + } + + + /// + /// Find the position of a matching brace or INVALID_POSITION if no match. + /// + /// + /// Autogenerated: IGEN03 + public int BraceMatch(int pos) + { + return (int)FastPerform(2353, (uint)pos, 0); + } + + + /// + /// Sets the current caret position to be the search anchor. + /// + /// + /// Autogenerated: IGEN01 + public void SearchAnchor() + { + FastPerform(2366, 0, 0); + } + + + /// + /// Find some text starting at the search anchor. + /// Does not ensure the selection is visible. + /// + /// + /// Autogenerated: IGEN22 + unsafe public int SearchNext(int flags, string text) + { + if (text == null || text.Equals("")) + text = "\0\0"; + + fixed (byte* b = System.Text.UTF8Encoding.UTF8.GetBytes(text)) + return (int)FastPerform(2367, (uint)flags, (uint)b); + } + + + /// + /// Find some text starting at the search anchor and moving backwards. + /// Does not ensure the selection is visible. + /// + /// + /// Autogenerated: IGEN22 + unsafe public int SearchPrev(int flags, string text) + { + if (text == null || text.Equals("")) + text = "\0\0"; + + fixed (byte* b = System.Text.UTF8Encoding.UTF8.GetBytes(text)) + return (int)FastPerform(2368, (uint)flags, (uint)b); + } + + + /// + /// Set whether a pop up menu is displayed automatically when the user presses + /// the wrong mouse button. + /// + /// + /// Autogenerated: IGEN02 + public void UsePopUp(bool allowPopUp) + { + FastPerform(2371, (uint)(allowPopUp ? 1 : 0), 0); + } + + + /// + /// Create a new document object. + /// Starts with reference count of 1 and not selected into editor. + /// Extend life of document. + /// + /// + /// Autogenerated: IGEN11 + public void AddRefDocument(int doc) + { + FastPerform(2376, 0, (uint)doc); + } + + + /// + /// Create a new document object. + /// Starts with reference count of 1 and not selected into editor. + /// Extend life of document. + /// Release a reference to the document, deleting document if it fades to black. + /// + /// + /// Autogenerated: IGEN11 + public void ReleaseDocument(int doc) + { + FastPerform(2377, 0, (uint)doc); + } + + + /// + /// Move to the previous change in capitalisation. + /// + /// + /// Autogenerated: IGEN01 + public void WordPartLeft() + { + FastPerform(2390, 0, 0); + } + + + /// + /// Move to the previous change in capitalisation. + /// Move to the previous change in capitalisation extending selection + /// to new caret position. + /// + /// + /// Autogenerated: IGEN01 + public void WordPartLeftExtend() + { + FastPerform(2391, 0, 0); + } + + + /// + /// Move to the previous change in capitalisation. + /// Move to the previous change in capitalisation extending selection + /// to new caret position. + /// Move to the change next in capitalisation. + /// + /// + /// Autogenerated: IGEN01 + public void WordPartRight() + { + FastPerform(2392, 0, 0); + } + + + /// + /// Move to the previous change in capitalisation. + /// Move to the previous change in capitalisation extending selection + /// to new caret position. + /// Move to the change next in capitalisation. + /// Move to the next change in capitalisation extending selection + /// to new caret position. + /// + /// + /// Autogenerated: IGEN01 + public void WordPartRightExtend() + { + FastPerform(2393, 0, 0); + } + + + /// + /// Constants for use with SetVisiblePolicy, similar to SetCaretPolicy. + /// Set the way the display area is determined when a particular line + /// is to be moved to by Find, FindNext, GotoLine, etc. + /// + /// + /// Autogenerated: IGEN16 + public void SetVisiblePolicy(int visiblePolicy, int visibleSlop) + { + FastPerform(2394, (uint)visiblePolicy, (uint)visibleSlop); + } + + + /// + /// Delete back from the current position to the start of the line. + /// + /// + /// Autogenerated: IGEN01 + public void DelLineLeft() + { + FastPerform(2395, 0, 0); + } + + + /// + /// Delete forwards from the current position to the end of the line. + /// + /// + /// Autogenerated: IGEN01 + public void DelLineRight() + { + FastPerform(2396, 0, 0); + } + + + /// + /// Set the last x chosen value to be the caret x position. + /// + /// + /// Autogenerated: IGEN01 + public void ChooseCaretX() + { + FastPerform(2399, 0, 0); + } + + + /// + /// Set the focus to this Scintilla widget. + /// GTK+ Specific. + /// + /// + /// Autogenerated: IGEN01 + public void GrabFocus() + { + FastPerform(2400, 0, 0); + } + + + /// + /// Set the way the caret is kept visible when going sideway. + /// The exclusion zone is given in pixels. + /// + /// + /// Autogenerated: IGEN16 + public void SetXCaretPolicy(int caretPolicy, int caretSlop) + { + FastPerform(2402, (uint)caretPolicy, (uint)caretSlop); + } + + + /// + /// Set the way the line the caret is on is kept visible. + /// The exclusion zone is given in lines. + /// + /// + /// Autogenerated: IGEN16 + public void SetYCaretPolicy(int caretPolicy, int caretSlop) + { + FastPerform(2403, (uint)caretPolicy, (uint)caretSlop); + } + + + /// + /// Move caret between paragraphs (delimited by empty lines). + /// + /// + /// Autogenerated: IGEN01 + public void ParaDown() + { + FastPerform(2413, 0, 0); + } + + + /// + /// Move caret between paragraphs (delimited by empty lines). + /// + /// + /// Autogenerated: IGEN01 + public void ParaDownExtend() + { + FastPerform(2414, 0, 0); + } + + + /// + /// Move caret between paragraphs (delimited by empty lines). + /// + /// + /// Autogenerated: IGEN01 + public void ParaUp() + { + FastPerform(2415, 0, 0); + } + + + /// + /// Move caret between paragraphs (delimited by empty lines). + /// + /// + /// Autogenerated: IGEN01 + public void ParaUpExtend() + { + FastPerform(2416, 0, 0); + } + + + /// + /// Given a valid document position, return the previous position taking code + /// page into account. Returns 0 if passed 0. + /// + /// + /// Autogenerated: IGEN03 + public int PositionBefore(int pos) + { + return (int)FastPerform(2417, (uint)pos, 0); + } + + + /// + /// Given a valid document position, return the next position taking code + /// page into account. Maximum value returned is the last position in the document. + /// + /// + /// Autogenerated: IGEN03 + public int PositionAfter(int pos) + { + return (int)FastPerform(2418, (uint)pos, 0); + } + + + /// + /// Copy a range of text to the clipboard. Positions are clipped into the document. + /// + /// + /// Autogenerated: IGEN16 + public void CopyRange(int start, int end) + { + FastPerform(2419, (uint)start, (uint)end); + } + + + /// + /// Copy argument text to the clipboard. + /// + /// + /// Autogenerated: IGEN22 + unsafe public void CopyText(int length, string text) + { + if (text == null || text.Equals("")) + text = "\0\0"; + + fixed (byte* b = System.Text.UTF8Encoding.UTF8.GetBytes(text)) + FastPerform(2420, (uint)length, (uint)b); + } + + + /// + /// Retrieve the position of the start of the selection at the given line (INVALID_POSITION if no selection on this line). + /// + /// + /// Autogenerated: IGEN03 + public int GetLineSelStartPosition(int line) + { + return (int)FastPerform(2424, (uint)line, 0); + } + + + /// + /// Retrieve the position of the end of the selection at the given line (INVALID_POSITION if no selection on this line). + /// + /// + /// Autogenerated: IGEN03 + public int GetLineSelEndPosition(int line) + { + return (int)FastPerform(2425, (uint)line, 0); + } + + + /// + /// Move caret down one line, extending rectangular selection to new caret position. + /// + /// + /// Autogenerated: IGEN01 + public void LineDownRectExtend() + { + FastPerform(2426, 0, 0); + } + + + /// + /// Move caret up one line, extending rectangular selection to new caret position. + /// + /// + /// Autogenerated: IGEN01 + public void LineUpRectExtend() + { + FastPerform(2427, 0, 0); + } + + + /// + /// Move caret left one character, extending rectangular selection to new caret position. + /// + /// + /// Autogenerated: IGEN01 + public void CharLeftRectExtend() + { + FastPerform(2428, 0, 0); + } + + + /// + /// Move caret right one character, extending rectangular selection to new caret position. + /// + /// + /// Autogenerated: IGEN01 + public void CharRightRectExtend() + { + FastPerform(2429, 0, 0); + } + + + /// + /// Move caret to first position on line, extending rectangular selection to new caret position. + /// + /// + /// Autogenerated: IGEN01 + public void HomeRectExtend() + { + FastPerform(2430, 0, 0); + } + + + /// + /// Move caret to before first visible character on line. + /// If already there move to first character on line. + /// In either case, extend rectangular selection to new caret position. + /// + /// + /// Autogenerated: IGEN01 + public void VCHomeRectExtend() + { + FastPerform(2431, 0, 0); + } + + + /// + /// Move caret to last position on line, extending rectangular selection to new caret position. + /// + /// + /// Autogenerated: IGEN01 + public void LineEndRectExtend() + { + FastPerform(2432, 0, 0); + } + + + /// + /// Move caret one page up, extending rectangular selection to new caret position. + /// + /// + /// Autogenerated: IGEN01 + public void PageUpRectExtend() + { + FastPerform(2433, 0, 0); + } + + + /// + /// Move caret one page down, extending rectangular selection to new caret position. + /// + /// + /// Autogenerated: IGEN01 + public void PageDownRectExtend() + { + FastPerform(2434, 0, 0); + } + + + /// + /// Move caret to top of page, or one page up if already at top of page. + /// + /// + /// Autogenerated: IGEN01 + public void StutteredPageUp() + { + FastPerform(2435, 0, 0); + } + + + /// + /// Move caret to top of page, or one page up if already at top of page, extending selection to new caret position. + /// + /// + /// Autogenerated: IGEN01 + public void StutteredPageUpExtend() + { + FastPerform(2436, 0, 0); + } + + + /// + /// Move caret to bottom of page, or one page down if already at bottom of page. + /// + /// + /// Autogenerated: IGEN01 + public void StutteredPageDown() + { + FastPerform(2437, 0, 0); + } + + + /// + /// Move caret to bottom of page, or one page down if already at bottom of page, extending selection to new caret position. + /// + /// + /// Autogenerated: IGEN01 + public void StutteredPageDownExtend() + { + FastPerform(2438, 0, 0); + } + + + /// + /// Move caret left one word, position cursor at end of word. + /// + /// + /// Autogenerated: IGEN01 + public void WordLeftEnd() + { + FastPerform(2439, 0, 0); + } + + + /// + /// Move caret left one word, position cursor at end of word, extending selection to new caret position. + /// + /// + /// Autogenerated: IGEN01 + public void WordLeftEndExtend() + { + FastPerform(2440, 0, 0); + } + + + /// + /// Move caret right one word, position cursor at end of word. + /// + /// + /// Autogenerated: IGEN01 + public void WordRightEnd() + { + FastPerform(2441, 0, 0); + } + + + /// + /// Move caret right one word, position cursor at end of word, extending selection to new caret position. + /// + /// + /// Autogenerated: IGEN01 + public void WordRightEndExtend() + { + FastPerform(2442, 0, 0); + } + + + /// + /// Reset the set of characters for whitespace and word characters to the defaults. + /// + /// + /// Autogenerated: IGEN01 + public void SetCharsDefault() + { + FastPerform(2444, 0, 0); + } + + + /// + /// Enlarge the document to a particular size of text bytes. + /// + /// + /// Autogenerated: IGEN03 + public void Allocate(int bytes) + { + FastPerform(2446, (uint)bytes, 0); + } + + + /// + /// Start notifying the container of all key presses and commands. + /// + /// + /// Autogenerated: IGEN01 + public void StartRecord() + { + FastPerform(3001, 0, 0); + } + + + /// + /// Stop notifying the container of all key presses and commands. + /// + /// + /// Autogenerated: IGEN01 + public void StopRecord() + { + FastPerform(3002, 0, 0); + } + + + /// + /// Colourise a segment of the document using the current lexing language. + /// + /// + /// Autogenerated: IGEN16 + public void Colourise(int start, int end) + { + FastPerform(4003, (uint)start, (uint)end); + } + + + /// + /// Load a lexer library (dll / so). + /// + /// + /// Autogenerated: IGEN04 + unsafe public void LoadLexerLibrary(string path) + { + if (path == null || path.Equals("")) + path = "\0\0"; + + fixed (byte* b = System.Text.UTF8Encoding.UTF8.GetBytes(path)) + FastPerform(4007, 0, (uint)b); + } + + #endregion + } +} diff --git a/Source/Core/Controls/ScriptDocumentTab.cs b/Source/Core/Controls/ScriptDocumentTab.cs new file mode 100644 index 0000000..8b84bd9 --- /dev/null +++ b/Source/Core/Controls/ScriptDocumentTab.cs @@ -0,0 +1,323 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Text; +using System.Windows.Forms; +using CodeImp.DoomBuilder.Windows; +using Microsoft.Win32; +using System.Diagnostics; +using CodeImp.DoomBuilder.Data; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Config; +using CodeImp.DoomBuilder.Types; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Compilers; + +#endregion + +namespace CodeImp.DoomBuilder.Controls +{ + internal abstract class ScriptDocumentTab : TabPage + { + #region ================== Constants + + private const int EDITOR_BORDER_TOP = 8; + private const int EDITOR_BORDER_BOTTOM = 4; + private const int EDITOR_BORDER_LEFT = 4; + private const int EDITOR_BORDER_RIGHT = 4; + + #endregion + + #region ================== Variables + + // The script edit control + protected ScriptEditorControl editor; + + // Derived classes must set this! + protected ScriptConfiguration config; + + // The panel we're on + protected ScriptEditorPanel panel; + + #endregion + + #region ================== Properties + + public virtual bool ExplicitSave { get { return true; } } + public virtual bool IsSaveAsRequired { get { return true; } } + public virtual bool IsClosable { get { return true; } } + public virtual bool IsReconfigurable { get { return true; } } + public virtual string Filename { get { return null; } } + public ScriptEditorPanel Panel { get { return panel; } } + public bool IsChanged { get { return editor.IsChanged; } } + public int SelectionStart { get { return editor.SelectionStart; } set { editor.SelectionStart = value; } } + public int SelectionEnd { get { return editor.SelectionEnd; } set { editor.SelectionEnd = value; } } + public ScriptConfiguration Config { get { return config; } } + + #endregion + + #region ================== Constructor + + // Constructor + public ScriptDocumentTab(ScriptEditorPanel panel) + { + // Keep panel + this.panel = panel; + + // Make the script control + editor = new ScriptEditorControl(); + editor.Location = new Point(EDITOR_BORDER_LEFT, EDITOR_BORDER_TOP); + editor.Size = new Size(this.ClientSize.Width - EDITOR_BORDER_LEFT - EDITOR_BORDER_RIGHT, + this.ClientSize.Height - EDITOR_BORDER_TOP - EDITOR_BORDER_BOTTOM); + editor.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right; + editor.Name = "editor"; + editor.TabStop = true; + editor.TabIndex = 0; + this.Controls.Add(editor); + + // Bind events + editor.OnExplicitSaveTab += panel.ExplicitSaveCurrentTab; + editor.OnOpenScriptBrowser += panel.OpenBrowseScript; + editor.OnOpenFindAndReplace += panel.OpenFindAndReplace; + editor.OnFindNext += panel.FindNext; + } + + // Disposer + protected override void Dispose(bool disposing) + { + // Remove events + editor.OnExplicitSaveTab -= panel.ExplicitSaveCurrentTab; + editor.OnOpenScriptBrowser -= panel.OpenBrowseScript; + editor.OnOpenFindAndReplace -= panel.OpenFindAndReplace; + editor.OnFindNext -= panel.FindNext; + + base.Dispose(disposing); + } + + #endregion + + #region ================== Methods + + // This launches keyword help website + public void LaunchKeywordHelp() + { + editor.LaunchKeywordHelp(); + } + + // This refreshes the style settings + public virtual void RefreshSettings() + { + editor.RefreshStyle(); + } + + // This moves the caret to the given line + public virtual void MoveToLine(int linenumber) + { + editor.MoveToLine(linenumber); + } + + // This clears all marks + public virtual void ClearMarks() + { + editor.ClearMarks(); + } + + // This creates error marks for errors that apply to this file + public virtual void MarkScriptErrors(IEnumerable errors) + { + // Clear all marks + ClearMarks(); + + // Go for all errors that apply to this script + foreach (CompilerError e in errors) + { + if (VerifyErrorForScript(e)) + { + // Add a mark on the line where this error occurred + editor.AddMark(e.linenumber); + } + } + } + + // This verifies if the specified error applies to this script + public virtual bool VerifyErrorForScript(CompilerError e) + { + return false; + } + + // This compiles the script + public virtual void Compile() + { + } + + // This saves the document (used for both explicit and implicit) + // Return true when successfully saved + public virtual bool Save() + { + return false; + } + + // This saves the document to a new file + // Return true when successfully saved + public virtual bool SaveAs(string filename) + { + return false; + } + + // This changes the script configurations + public virtual void ChangeScriptConfig(ScriptConfiguration newconfig) + { + } + + // Call this to set the tab title + protected void SetTitle(string title) + { + this.Text = title; + } + + // Perform undo + public void Undo() + { + editor.Undo(); + } + + // Perform redo + public void Redo() + { + editor.Redo(); + } + + // Perform cut + public void Cut() + { + editor.Cut(); + } + + // Perform copy + public void Copy() + { + editor.Copy(); + } + + // Perform paste + public void Paste() + { + editor.Paste(); + } + + // Find next result + public bool FindNext(FindReplaceOptions options) + { + byte[] data = editor.GetText(); + string text = Encoding.GetEncoding(config.CodePage).GetString(data); + StringComparison mode = options.CaseSensitive ? StringComparison.CurrentCulture : StringComparison.CurrentCultureIgnoreCase; + int startpos = Math.Max(editor.SelectionStart, editor.SelectionEnd); + bool wrapped = false; + + while (true) + { + int result = text.IndexOf(options.FindText, startpos, mode); + if (result > -1) + { + // Check to see if it is the whole word + if (options.WholeWord) + { + // Veryfy that we have found a whole word + string foundword = editor.GetWordAt(result + 1); + if (foundword.Length != options.FindText.Length) + { + startpos = result + 1; + result = -1; + } + } + + // Still ok? + if (result > -1) + { + // Select the result + editor.SelectionStart = result; + editor.SelectionEnd = result + options.FindText.Length; + editor.EnsureLineVisible(editor.LineFromPosition(editor.SelectionEnd)); + return true; + } + } + else + { + // If we haven't tried from the start, try from the start now + if ((startpos > 0) && !wrapped) + { + startpos = 0; + wrapped = true; + } + else + { + // Can't find it + return false; + } + } + } + } + + // This replaces the selection with the given text + public void ReplaceSelection(string replacement) + { + editor.ReplaceSelection(replacement); + } + + // This returns the selected text + public string GetSelectedText() + { + byte[] data = editor.GetText(); + string text = Encoding.GetEncoding(config.CodePage).GetString(data); + if (editor.SelectionStart < editor.SelectionEnd) + return text.Substring(editor.SelectionStart, editor.SelectionEnd - editor.SelectionStart); + else + return ""; + } + + #endregion + + #region ================== Events + + // Mouse released + protected override void OnMouseUp(MouseEventArgs e) + { + base.OnMouseUp(e); + + // Focus to the editor! + editor.Focus(); + editor.GrabFocus(); + } + + // Receiving focus? + protected override void OnGotFocus(EventArgs e) + { + base.OnGotFocus(e); + + // Focus to the editor! + editor.Focus(); + editor.GrabFocus(); + } + + #endregion + } +} diff --git a/Source/Core/Controls/ScriptEditorControl.Designer.cs b/Source/Core/Controls/ScriptEditorControl.Designer.cs new file mode 100644 index 0000000..4c715ed --- /dev/null +++ b/Source/Core/Controls/ScriptEditorControl.Designer.cs @@ -0,0 +1,167 @@ +namespace CodeImp.DoomBuilder.Controls +{ + partial class ScriptEditorControl + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.functionbar = new System.Windows.Forms.ComboBox(); + this.scriptedit = new CodeImp.DoomBuilder.Controls.ScintillaControl(); + this.scriptpanel = new System.Windows.Forms.Panel(); + this.scriptpanel.SuspendLayout(); + this.SuspendLayout(); + // + // functionbar + // + this.functionbar.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.functionbar.FormattingEnabled = true; + this.functionbar.Items.AddRange(new object[] { + "Function1", + "Function2", + "Function3"}); + this.functionbar.Location = new System.Drawing.Point(0, 0); + this.functionbar.Name = "functionbar"; + this.functionbar.Size = new System.Drawing.Size(474, 21); + this.functionbar.TabIndex = 1; + this.functionbar.TabStop = false; + // + // scriptedit + // + this.scriptedit.AnchorPosition = 0; + this.scriptedit.AutoCMaximumHeight = 0; + this.scriptedit.AutoCMaximumWidth = 0; + this.scriptedit.AutoCSeparator = 0; + this.scriptedit.AutoCTypeSeparator = 0; + this.scriptedit.BackColor = System.Drawing.SystemColors.Window; + this.scriptedit.CaretFore = 0; + this.scriptedit.CaretLineBack = 0; + this.scriptedit.CaretPeriod = 0; + this.scriptedit.CaretWidth = 0; + this.scriptedit.CodePage = 0; + this.scriptedit.ControlCharSymbol = 0; + this.scriptedit.CurrentPos = 0; + this.scriptedit.CursorType = 0; + this.scriptedit.Dock = System.Windows.Forms.DockStyle.Fill; + this.scriptedit.DocPointer = 0; + this.scriptedit.EdgeColour = 0; + this.scriptedit.EdgeColumn = 0; + this.scriptedit.EdgeMode = 0; + this.scriptedit.EndAtLastLine = 0; + this.scriptedit.EndOfLineMode = CodeImp.DoomBuilder.Controls.ScriptEndOfLine.CRLF; + this.scriptedit.EOLMode = 0; + this.scriptedit.ExtraAscent = 0; + this.scriptedit.ExtraDescent = 0; + this.scriptedit.HighlightGuide = 0; + this.scriptedit.Indent = 0; + this.scriptedit.IndentationGuides = 0; + this.scriptedit.IsAutoCGetAutoHide = false; + this.scriptedit.IsAutoCGetCancelAtStart = false; + this.scriptedit.IsAutoCGetChooseSingle = false; + this.scriptedit.IsAutoCGetDropRestOfWord = false; + this.scriptedit.IsAutoCGetIgnoreCase = false; + this.scriptedit.IsBackSpaceUnIndents = false; + this.scriptedit.IsBufferedDraw = false; + this.scriptedit.IsCaretLineVisible = false; + this.scriptedit.IsFocus = false; + this.scriptedit.IsHScrollBar = false; + this.scriptedit.IsMouseDownCaptures = false; + this.scriptedit.IsOvertype = false; + this.scriptedit.IsReadOnly = false; + this.scriptedit.IsTabIndents = false; + this.scriptedit.IsTwoPhaseDraw = false; + this.scriptedit.IsUndoCollection = false; + this.scriptedit.IsUsePalette = false; + this.scriptedit.IsUseTabs = false; + this.scriptedit.IsViewEOL = false; + this.scriptedit.IsVScrollBar = false; + this.scriptedit.LayoutCache = 0; + this.scriptedit.Lexer = 0; + this.scriptedit.Location = new System.Drawing.Point(0, 0); + this.scriptedit.MarginLeft = 0; + this.scriptedit.MarginRight = 0; + this.scriptedit.ModEventMask = 0; + this.scriptedit.MouseDwellTime = 0; + this.scriptedit.Name = "scriptedit"; + this.scriptedit.PrintColourMode = 0; + this.scriptedit.PrintMagnification = 0; + this.scriptedit.PrintWrapMode = 0; + this.scriptedit.ScrollWidth = 0; + this.scriptedit.SearchFlags = 0; + this.scriptedit.SelectionEnd = 0; + this.scriptedit.SelectionMode = 0; + this.scriptedit.SelectionStart = 0; + this.scriptedit.Size = new System.Drawing.Size(470, 377); + this.scriptedit.Status = 0; + this.scriptedit.StyleBits = 0; + this.scriptedit.TabIndex = 0; + this.scriptedit.TabStop = false; + this.scriptedit.TabWidth = 0; + this.scriptedit.TargetEnd = 0; + this.scriptedit.TargetStart = 0; + this.scriptedit.ViewWhitespace = CodeImp.DoomBuilder.Controls.ScriptWhiteSpace.Invisible; + this.scriptedit.ViewWS = 0; + this.scriptedit.WrapMode = 0; + this.scriptedit.WrapStartIndent = 0; + this.scriptedit.WrapVisualFlags = 0; + this.scriptedit.WrapVisualFlagsLocation = 0; + this.scriptedit.XOffset = 0; + this.scriptedit.ZoomLevel = 0; + this.scriptedit.KeyUp += new System.Windows.Forms.KeyEventHandler(this.scriptedit_KeyUp); + this.scriptedit.KeyDown += new System.Windows.Forms.KeyEventHandler(this.scriptedit_KeyDown); + // + // scriptpanel + // + this.scriptpanel.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.scriptpanel.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D; + this.scriptpanel.Controls.Add(this.scriptedit); + this.scriptpanel.Location = new System.Drawing.Point(0, 27); + this.scriptpanel.Name = "scriptpanel"; + this.scriptpanel.Size = new System.Drawing.Size(474, 381); + this.scriptpanel.TabIndex = 2; + // + // ScriptEditorControl + // + this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; + this.Controls.Add(this.scriptpanel); + this.Controls.Add(this.functionbar); + this.Name = "ScriptEditorControl"; + this.Size = new System.Drawing.Size(474, 408); + this.scriptpanel.ResumeLayout(false); + this.ResumeLayout(false); + + } + + #endregion + + private ScintillaControl scriptedit; + private System.Windows.Forms.ComboBox functionbar; + private System.Windows.Forms.Panel scriptpanel; + } +} diff --git a/Source/Core/Controls/ScriptEditorControl.cs b/Source/Core/Controls/ScriptEditorControl.cs new file mode 100644 index 0000000..058b39c --- /dev/null +++ b/Source/Core/Controls/ScriptEditorControl.cs @@ -0,0 +1,822 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Text; +using System.Windows.Forms; +using Microsoft.Win32; +using System.Drawing.Drawing2D; +using CodeImp.DoomBuilder.Config; +using System.IO; +using System.Reflection; +using CodeImp.DoomBuilder.IO; +using System.Collections; +using System.Globalization; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Properties; + +#endregion + +namespace CodeImp.DoomBuilder.Controls +{ + internal partial class ScriptEditorControl : UserControl + { + #region ================== Constants + + private const string LEXERS_RESOURCE = "Lexers.cfg"; + private const int DEFAULT_STYLE = (int)ScriptStylesCommon.Default; + private const int MAX_BACKTRACK_LENGTH = 200; + + // Index for registered images + private enum ImageIndex : int + { + ScriptConstant = 0, + ScriptKeyword = 1, + ScriptError = 2 + } + + #endregion + + #region ================== Delegates / Events + + public delegate void ExplicitSaveTabDelegate(); + public delegate void OpenScriptBrowserDelegate(); + public delegate void OpenFindReplaceDelegate(); + public delegate void FindNextDelegate(); + + public event ExplicitSaveTabDelegate OnExplicitSaveTab; + public event OpenScriptBrowserDelegate OnOpenScriptBrowser; + public event OpenFindReplaceDelegate OnOpenFindAndReplace; + public event FindNextDelegate OnFindNext; + + #endregion + + #region ================== Variables + + // Script configuration + private ScriptConfiguration scriptconfig; + + // List of keywords and constants, sorted as uppercase + private string autocompletestring; + + // Style translation from Scintilla style to ScriptStyleType + private Dictionary stylelookup; + + // Current position information + private string curfunctionname = ""; + private int curargumentindex = 0; + private int curfunctionstartpos = 0; + + // Status + private bool changed; + + #endregion + + #region ================== Properties + + public bool IsChanged { get { return changed; } set { changed = value; } } + public int Position { get { return scriptedit.CurrentPos; } set { scriptedit.CurrentPos = value; } } + public int SelectionStart { get { return scriptedit.SelectionStart; } set { scriptedit.SelectionStart = value; } } + public int SelectionEnd { get { return scriptedit.SelectionEnd; } set { scriptedit.SelectionEnd = value; } } + + #endregion + + #region ================== Contructor / Disposer + + // Constructor + public ScriptEditorControl() + { + // Initialize + InitializeComponent(); + + // Script editor properties + // Unfortunately, these cannot be set using the designer + // because the control is not really loaded in design mode + scriptedit.AutoCMaximumHeight = 8; + scriptedit.AutoCSeparator = ' '; + scriptedit.AutoCTypeSeparator = '?'; + scriptedit.AutoCSetFillUps("\r\n();[]"); // I should put this in the script configs + scriptedit.CaretWidth = 2; + scriptedit.EndAtLastLine = 1; + scriptedit.EndOfLineMode = ScriptEndOfLine.CRLF; + scriptedit.IsAutoCGetChooseSingle = true; + scriptedit.IsAutoCGetIgnoreCase = true; + scriptedit.IsBackSpaceUnIndents = true; + scriptedit.IsBufferedDraw = true; + scriptedit.IsCaretLineVisible = false; + scriptedit.IsHScrollBar = true; + scriptedit.IndentationGuides = (int)ScriptIdentGuides.None; + scriptedit.IsMouseDownCaptures = true; + scriptedit.IsTabIndents = true; + scriptedit.IsUndoCollection = true; + scriptedit.IsUseTabs = true; + scriptedit.IsViewEOL = false; + scriptedit.IsVScrollBar = true; + scriptedit.SetFoldFlags((int)ScriptFoldFlag.Box); + scriptedit.TabWidth = 4; + scriptedit.Indent = 4; + scriptedit.ExtraAscent = 1; + scriptedit.ExtraDescent = 1; + scriptedit.CursorType = -1; + + // Symbol margin + scriptedit.SetMarginTypeN(0, (int)ScriptMarginType.Symbol); + scriptedit.SetMarginWidthN(0, 20); + scriptedit.SetMarginMaskN(0, -1); // all + + // Line numbers margin + scriptedit.SetMarginTypeN(1, (int)ScriptMarginType.Number); + scriptedit.SetMarginWidthN(1, 40); + scriptedit.SetMarginMaskN(1, 0); // none + + // Spacing margin + scriptedit.SetMarginTypeN(2, (int)ScriptMarginType.Symbol); + scriptedit.SetMarginWidthN(2, 5); + scriptedit.SetMarginMaskN(2, 0); // none + + // Setup with default script config + // Disabled, the form designer doesn't like this + //SetupStyles(new ScriptConfiguration()); + + // Images + RegisterAutoCompleteImage(ImageIndex.ScriptConstant, Resources.ScriptConstant); + RegisterAutoCompleteImage(ImageIndex.ScriptKeyword, Resources.ScriptKeyword); + RegisterMarkerImage(ImageIndex.ScriptError, Resources.ScriptError); + + // Events + scriptedit.ModEventMask = 0x7FFFF; // Which events to receive (see also ScriptModificationFlags) + scriptedit.Modified += new ScintillaControl.ModifiedHandler(scriptedit_Modified); + } + + #endregion + + #region ================== Methods + + // This launches keyword help website + public void LaunchKeywordHelp() + { + string helpsite = scriptconfig.KeywordHelp; + string currentword = GetCurrentWord(); + if (!string.IsNullOrEmpty(currentword) && (currentword.Length > 1) && !string.IsNullOrEmpty(helpsite)) + { + currentword = scriptconfig.GetKeywordCase(currentword); + helpsite = helpsite.Replace("%K", currentword); + General.OpenWebsite(helpsite); + } + } + + // This replaces the selection with the given text + public void ReplaceSelection(string replacement) + { + Encoding encoder = Encoding.GetEncoding(scriptedit.CodePage); + string text = encoder.GetString(GetText()); + int selectionstart = scriptedit.SelectionStart; + + // Make new text + StringBuilder newtext = new StringBuilder(text.Length + replacement.Length); + newtext.Append(text.Substring(0, scriptedit.SelectionStart)); + newtext.Append(replacement); + newtext.Append(text.Substring(scriptedit.SelectionEnd)); + + SetText(encoder.GetBytes(newtext.ToString())); + + // Adjust selection + scriptedit.SelectionStart = selectionstart; + scriptedit.SelectionEnd = selectionstart + replacement.Length; + } + + // This moves the caret to a given line and ensures the line is visible + public void MoveToLine(int linenumber) + { + scriptedit.GotoLine(linenumber); + EnsureLineVisible(linenumber); + } + + // This makes sure a line is visible + public void EnsureLineVisible(int linenumber) + { + scriptedit.EnsureVisibleEnforcePolicy(linenumber); + } + + // This returns the line for a position + public int LineFromPosition(int position) + { + return scriptedit.LineFromPosition(position); + } + + // This clears all marks + public void ClearMarks() + { + scriptedit.MarkerDeleteAll((int)ImageIndex.ScriptError); + } + + // This adds a mark on the given line + public void AddMark(int linenumber) + { + scriptedit.MarkerAdd(linenumber, (int)ImageIndex.ScriptError); + } + + // This refreshes the style setup + public void RefreshStyle() + { + // Re-setup with the same config + SetupStyles(scriptconfig); + } + + // This sets up the script editor with a script configuration + public void SetupStyles(ScriptConfiguration config) + { + Stream lexersdata; + StreamReader lexersreader; + Configuration lexercfg = new Configuration(); + SortedList autocompletelist; + string[] resnames; + int imageindex; + + // Make collections + stylelookup = new Dictionary(); + autocompletelist = new SortedList(StringComparer.Ordinal); + + // Keep script configuration + if (scriptconfig != config) scriptconfig = config; + + // Find a resource named Lexers.cfg + resnames = General.ThisAssembly.GetManifestResourceNames(); + foreach (string rn in resnames) + { + // Found one? + if (rn.EndsWith(LEXERS_RESOURCE, StringComparison.InvariantCultureIgnoreCase)) + { + // Get a stream from the resource + lexersdata = General.ThisAssembly.GetManifestResourceStream(rn); + lexersreader = new StreamReader(lexersdata, Encoding.ASCII); + + // Load configuration from stream + lexercfg.InputConfiguration(lexersreader.ReadToEnd()); + + // Done with the resource + lexersreader.Dispose(); + lexersdata.Dispose(); + } + } + + // Check if specified lexer exists and set the lexer to use + string lexername = "lexer" + scriptconfig.Lexer.ToString(CultureInfo.InvariantCulture); + if (!lexercfg.SettingExists(lexername)) throw new InvalidOperationException("Unknown lexer " + scriptconfig.Lexer + " specified in script configuration!"); + scriptedit.Lexer = scriptconfig.Lexer; + + // Set the default style and settings + scriptedit.StyleSetFont(DEFAULT_STYLE, General.Settings.ScriptFontName); + scriptedit.StyleSetSize(DEFAULT_STYLE, General.Settings.ScriptFontSize); + scriptedit.StyleSetBold(DEFAULT_STYLE, General.Settings.ScriptFontBold); + scriptedit.StyleSetItalic(DEFAULT_STYLE, false); + scriptedit.StyleSetUnderline(DEFAULT_STYLE, false); + scriptedit.StyleSetCase(DEFAULT_STYLE, ScriptCaseVisible.Mixed); + scriptedit.StyleSetFore(DEFAULT_STYLE, General.Colors.PlainText.ToColorRef()); + scriptedit.StyleSetBack(DEFAULT_STYLE, General.Colors.ScriptBackground.ToColorRef()); + scriptedit.CaretPeriod = SystemInformation.CaretBlinkTime; + scriptedit.CaretFore = General.Colors.ScriptBackground.Inverse().ToColorRef(); + scriptedit.StyleBits = 7; + + // These don't work? + scriptedit.TabWidth = General.Settings.ScriptTabWidth; + scriptedit.IsUseTabs = false; + scriptedit.IsTabIndents = true; + scriptedit.Indent = General.Settings.ScriptTabWidth; + scriptedit.IsBackSpaceUnIndents = true; + + // This applies the default style to all styles + scriptedit.StyleClearAll(); + + // Set the code page to use + scriptedit.CodePage = scriptconfig.CodePage; + + // Set the default to something normal (this is used by the autocomplete list) + scriptedit.StyleSetFont(DEFAULT_STYLE, this.Font.Name); + scriptedit.StyleSetBold(DEFAULT_STYLE, this.Font.Bold); + scriptedit.StyleSetItalic(DEFAULT_STYLE, this.Font.Italic); + scriptedit.StyleSetUnderline(DEFAULT_STYLE, this.Font.Underline); + scriptedit.StyleSetSize(DEFAULT_STYLE, (int)Math.Round(this.Font.SizeInPoints)); + + // Set style for linenumbers and margins + scriptedit.StyleSetBack((int)ScriptStylesCommon.LineNumber, General.Colors.ScriptBackground.ToColorRef()); + + // Clear all keywords + for (int i = 0; i < 9; i++) scriptedit.KeyWords(i, null); + + // Now go for all elements in the lexer configuration + // We are looking for the numeric keys, because these are the + // style index to set and the value is our ScriptStyleType + IDictionary dic = lexercfg.ReadSetting(lexername, new Hashtable()); + foreach (DictionaryEntry de in dic) + { + // Check if this is a numeric key + int stylenum = -1; + if (int.TryParse(de.Key.ToString(), out stylenum)) + { + // Add style to lookup table + stylelookup.Add(stylenum, (ScriptStyleType)(int)de.Value); + + // Apply color to style + int colorindex = 0; + switch ((ScriptStyleType)(int)de.Value) + { + case ScriptStyleType.PlainText: colorindex = ColorCollection.PLAINTEXT; break; + case ScriptStyleType.Comment: colorindex = ColorCollection.COMMENTS; break; + case ScriptStyleType.Constant: colorindex = ColorCollection.CONSTANTS; break; + case ScriptStyleType.Keyword: colorindex = ColorCollection.KEYWORDS; break; + case ScriptStyleType.LineNumber: colorindex = ColorCollection.LINENUMBERS; break; + case ScriptStyleType.Literal: colorindex = ColorCollection.LITERALS; break; + default: colorindex = ColorCollection.PLAINTEXT; break; + } + scriptedit.StyleSetFore(stylenum, General.Colors.Colors[colorindex].ToColorRef()); + } + } + + // Create the keywords list and apply it + imageindex = (int)ImageIndex.ScriptKeyword; + int keywordsindex = lexercfg.ReadSetting(lexername + ".keywordsindex", -1); + if (keywordsindex > -1) + { + StringBuilder keywordslist = new StringBuilder(""); + foreach (string k in scriptconfig.Keywords) + { + if (keywordslist.Length > 0) keywordslist.Append(" "); + keywordslist.Append(k); + autocompletelist.Add(k.ToUpperInvariant(), k + "?" + imageindex.ToString(CultureInfo.InvariantCulture)); + } + string words = keywordslist.ToString(); + scriptedit.KeyWords(keywordsindex, words.ToLowerInvariant()); + } + + // Create the constants list and apply it + imageindex = (int)ImageIndex.ScriptConstant; + int constantsindex = lexercfg.ReadSetting(lexername + ".constantsindex", -1); + if (constantsindex > -1) + { + StringBuilder constantslist = new StringBuilder(""); + foreach (string c in scriptconfig.Constants) + { + if (constantslist.Length > 0) constantslist.Append(" "); + constantslist.Append(c); + autocompletelist.Add(c.ToUpperInvariant(), c + "?" + imageindex.ToString(CultureInfo.InvariantCulture)); + } + string words = constantslist.ToString(); + scriptedit.KeyWords(constantsindex, words.ToLowerInvariant()); + } + + // Sort the autocomplete list + List autocompleteplainlist = new List(autocompletelist.Values); + autocompletestring = string.Join(" ", autocompleteplainlist.ToArray()); + + // Show/hide the functions bar + functionbar.Visible = (scriptconfig.FunctionRegEx.Length > 0); + + // Rearrange the layout + scriptedit.ClearDocumentStyle(); + scriptedit.SetText(scriptedit.GetText(scriptedit.TextSize)); + this.PerformLayout(); + } + + + // This returns the current word (where the caret is at) + public string GetCurrentWord() + { + return GetWordAt(scriptedit.CurrentPos); + } + + + // This returns the word at the given position + public string GetWordAt(int position) + { + int wordstart = scriptedit.WordStartPosition(position, true); + int wordend = scriptedit.WordEndPosition(position, true); + + // Decode the text + byte[] scripttextdata = scriptedit.GetText(scriptedit.TextSize); + Encoding encoder = Encoding.GetEncoding(scriptedit.CodePage); + string scripttext = encoder.GetString(scripttextdata); + + if (wordstart < wordend) + return scripttext.Substring(wordstart, wordend - wordstart); + else + return ""; + } + + + // This returns the ScriptStyleType for a given Scintilla style + private ScriptStyleType GetScriptStyle(int scintillastyle) + { + if (stylelookup.ContainsKey(scintillastyle)) + return stylelookup[scintillastyle]; + else + return ScriptStyleType.PlainText; + } + + + // This gathers information about the current caret position + private void UpdatePositionInfo() + { + int bracketlevel = 0; // bracket level counting + int argindex = 0; // function argument counting + int limitpos; // lowest position we'll backtrack to + int pos = scriptedit.CurrentPos; + + // Decode the text + byte[] scripttextdata = scriptedit.GetText(scriptedit.TextSize); + Encoding encoder = Encoding.GetEncoding(scriptedit.CodePage); + string scripttext = encoder.GetString(scripttextdata); + + // Reset position info + curfunctionname = ""; + curargumentindex = 0; + curfunctionstartpos = 0; + + // Determine lowest backtrack position + limitpos = scriptedit.CurrentPos - MAX_BACKTRACK_LENGTH; + if (limitpos < 0) limitpos = 0; + + // We can only do this when we have function syntax information + if ((scriptconfig.ArgumentDelimiter.Length == 0) || (scriptconfig.FunctionClose.Length == 0) || + (scriptconfig.FunctionOpen.Length == 0) || (scriptconfig.Terminator.Length == 0)) return; + + // Get int versions of the function syntax informantion + int argumentdelimiter = scriptconfig.ArgumentDelimiter[0]; + int functionclose = scriptconfig.FunctionClose[0]; + int functionopen = scriptconfig.FunctionOpen[0]; + int terminator = scriptconfig.Terminator[0]; + + // Continue backtracking until we reached the limitpos + while (pos >= limitpos) + { + // Backtrack 1 character + pos--; + + // Get the style and character at this position + ScriptStyleType curstyle = GetScriptStyle(scriptedit.StyleAt(pos)); + int curchar = scriptedit.CharAt(pos); + + // Then meeting ) then increase bracket level + // When meeting ( then decrease bracket level + // When bracket level goes -1, then the next word should be the function name + // Only when at bracket level 0, count the comma's for argument index + + // TODO: + // Original code checked for scope character here and breaks if found + + // Check if in plain text or keyword + if ((curstyle == ScriptStyleType.PlainText) || (curstyle == ScriptStyleType.Keyword)) + { + // Closing bracket + if (curchar == functionclose) + { + bracketlevel++; + } + // Opening bracket + else if (curchar == functionopen) + { + bracketlevel--; + + // Out of the brackets? + if (bracketlevel < 0) + { + // Skip any whitespace before this bracket + do + { + // Backtrack 1 character + curchar = scriptedit.CharAt(--pos); + } + while ((pos >= limitpos) && ((curchar == ' ') || (curchar == '\t') || + (curchar == '\r') || (curchar == '\n'))); + + // NOTE: We may need to set onlyWordCharacters argument in the + // following calls to false to get any argument delimiter included, + // but this may also cause a valid keyword to be combined with other + // surrounding characters that do not belong to the keyword. + + // Find the word before this bracket + int wordstart = scriptedit.WordStartPosition(pos, true); + int wordend = scriptedit.WordEndPosition(pos, true); + string word = scripttext.Substring(wordstart, wordend - wordstart); + if (word.Length > 0) + { + // Check if this is an argument delimiter + // I can't remember why I did this, but I'll probably stumble + // upon the problem if this doesn't work right (see note above) + if (word[0] == argumentdelimiter) + { + // We are now in the parent function + bracketlevel++; + argindex = 0; + } + // Now check if this is a keyword + else if (scriptconfig.IsKeyword(word)) + { + // Found it! + curfunctionname = scriptconfig.GetKeywordCase(word); + curargumentindex = argindex; + curfunctionstartpos = wordstart; + break; + } + else + { + // Don't know this word + break; + } + } + } + } + // Argument delimiter + else if (curchar == argumentdelimiter) + { + // Only count these at brackt level 0 + if (bracketlevel == 0) argindex++; + } + // Terminator + else if (curchar == terminator) + { + // Can't find anything, break now + break; + } + } + } + } + + // This clears all undo levels + public void ClearUndoRedo() + { + scriptedit.EmptyUndoBuffer(); + } + + // This registers an XPM image for the autocomplete list + private unsafe void RegisterAutoCompleteImage(ImageIndex index, byte[] imagedata) + { + // Convert to string + string bigstring = Encoding.UTF8.GetString(imagedata); + + // Register image + scriptedit.RegisterImage((int)index, bigstring); + } + + // This registers an XPM image for the markes list + private unsafe void RegisterMarkerImage(ImageIndex index, byte[] imagedata) + { + // Convert to string + string bigstring = Encoding.UTF8.GetString(imagedata); + + // Register image + scriptedit.MarkerDefinePixmap((int)index, bigstring); + } + + // Perform undo + public void Undo() + { + scriptedit.Undo(); + } + + // Perform redo + public void Redo() + { + scriptedit.Redo(); + } + + // Perform cut + public void Cut() + { + scriptedit.Cut(); + } + + // Perform copy + public void Copy() + { + scriptedit.Copy(); + } + + // Perform paste + public void Paste() + { + scriptedit.Paste(); + } + + // This steals the focus (use with care!) + public void GrabFocus() + { + scriptedit.GrabFocus(); + } + + public byte[] GetText() + { + return scriptedit.GetText(scriptedit.TextSize); + } + + public void SetText(byte[] text) + { + scriptedit.SetText(text); + } + + #endregion + + #region ================== Events + + // Layout needs to be re-organized + protected override void OnLayout(LayoutEventArgs e) + { + base.OnLayout(e); + + // With or without functions bar? + if (functionbar.Visible) + { + scriptpanel.Top = functionbar.Bottom + 6; + scriptpanel.Height = this.ClientSize.Height - scriptpanel.Top; + } + else + { + scriptpanel.Top = 0; + scriptpanel.Height = this.ClientSize.Height; + } + } + + // Script modified + private void scriptedit_Modified(ScintillaControl pSender, int position, int modificationType, string text, int length, int linesAdded, int line, int foldLevelNow, int foldLevelPrev) + { + changed = true; + } + + // Key pressed down + private void scriptedit_KeyDown(object sender, KeyEventArgs e) + { + // These key combinations put odd characters in the script, so I disabled them + if ((e.KeyCode == Keys.Q) && ((e.Modifiers & Keys.Control) == Keys.Control)) e.Handled = true; + if ((e.KeyCode == Keys.W) && ((e.Modifiers & Keys.Control) == Keys.Control)) e.Handled = true; + if ((e.KeyCode == Keys.E) && ((e.Modifiers & Keys.Control) == Keys.Control)) e.Handled = true; + if ((e.KeyCode == Keys.R) && ((e.Modifiers & Keys.Control) == Keys.Control)) e.Handled = true; + if ((e.KeyCode == Keys.Y) && ((e.Modifiers & Keys.Control) == Keys.Control)) e.Handled = true; + if ((e.KeyCode == Keys.U) && ((e.Modifiers & Keys.Control) == Keys.Control)) e.Handled = true; + if ((e.KeyCode == Keys.I) && ((e.Modifiers & Keys.Control) == Keys.Control)) e.Handled = true; + if ((e.KeyCode == Keys.P) && ((e.Modifiers & Keys.Control) == Keys.Control)) e.Handled = true; + if ((e.KeyCode == Keys.A) && ((e.Modifiers & Keys.Control) == Keys.Control) && ((e.Modifiers & Keys.Shift) == Keys.Shift)) e.Handled = true; + if ((e.KeyCode == Keys.D) && ((e.Modifiers & Keys.Control) == Keys.Control)) e.Handled = true; + if ((e.KeyCode == Keys.G) && ((e.Modifiers & Keys.Control) == Keys.Control)) e.Handled = true; + if ((e.KeyCode == Keys.H) && ((e.Modifiers & Keys.Control) == Keys.Control)) e.Handled = true; + if ((e.KeyCode == Keys.J) && ((e.Modifiers & Keys.Control) == Keys.Control)) e.Handled = true; + if ((e.KeyCode == Keys.K) && ((e.Modifiers & Keys.Control) == Keys.Control)) e.Handled = true; + if ((e.KeyCode == Keys.L) && ((e.Modifiers & Keys.Control) == Keys.Control)) e.Handled = true; + if ((e.KeyCode == Keys.Z) && ((e.Modifiers & Keys.Control) == Keys.Control) && ((e.Modifiers & Keys.Shift) == Keys.Shift)) e.Handled = true; + if ((e.KeyCode == Keys.X) && ((e.Modifiers & Keys.Control) == Keys.Control) && ((e.Modifiers & Keys.Shift) == Keys.Shift)) e.Handled = true; + if ((e.KeyCode == Keys.C) && ((e.Modifiers & Keys.Control) == Keys.Control) && ((e.Modifiers & Keys.Shift) == Keys.Shift)) e.Handled = true; + if ((e.KeyCode == Keys.V) && ((e.Modifiers & Keys.Control) == Keys.Control) && ((e.Modifiers & Keys.Shift) == Keys.Shift)) e.Handled = true; + if ((e.KeyCode == Keys.M) && ((e.Modifiers & Keys.Control) == Keys.Control)) e.Handled = true; + + // F3 for Find Next + if ((e.KeyCode == Keys.F3) && (e.Modifiers == Keys.None)) + { + if (OnFindNext != null) OnFindNext(); + e.Handled = true; + } + + // F2 for Keyword Help + if ((e.KeyCode == Keys.F2) && (e.Modifiers == Keys.None)) + { + LaunchKeywordHelp(); + e.Handled = true; + } + + // CTRL+F for find & replace + if ((e.KeyCode == Keys.F) && ((e.Modifiers & Keys.Control) == Keys.Control)) + { + if (OnOpenFindAndReplace != null) OnOpenFindAndReplace(); + e.Handled = true; + } + + // CTRL+S for save + if ((e.KeyCode == Keys.S) && ((e.Modifiers & Keys.Control) == Keys.Control)) + { + if (OnExplicitSaveTab != null) OnExplicitSaveTab(); + e.Handled = true; + } + + // CTRL+O for open + if ((e.KeyCode == Keys.O) && ((e.Modifiers & Keys.Control) == Keys.Control)) + { + if (OnOpenScriptBrowser != null) OnOpenScriptBrowser(); + e.Handled = true; + } + + // CTRL+Space to autocomplete + if ((e.KeyCode == Keys.Space) && (e.Modifiers == Keys.Control)) + { + // Hide call tip if any + scriptedit.CallTipCancel(); + + // Show autocomplete + int currentpos = scriptedit.CurrentPos; + int wordstartpos = scriptedit.WordStartPosition(currentpos, true); + scriptedit.AutoCShow(currentpos - wordstartpos, autocompletestring); + + e.Handled = true; + } + } + + // Key released + private void scriptedit_KeyUp(object sender, KeyEventArgs e) + { + bool showcalltip = false; + int highlightstart = 0; + int highlightend = 0; + + // Enter pressed? + if ((e.KeyCode == Keys.Enter) && (e.Modifiers == Keys.None)) + { + // Do we want auto-indent? + if (General.Settings.ScriptAutoIndent) + { + // Get the current line index and check if its not the first line + int curline = scriptedit.LineFromPosition(scriptedit.CurrentPos); + if (curline > 0) + { + // Apply identation of the previous line to this line + int ident = scriptedit.GetLineIndentation(curline - 1); + int tabs = ident;// / scriptedit.Indent; + if (scriptedit.GetLineIndentation(curline) == 0) + { + scriptedit.SetLineIndentation(curline, ident); + scriptedit.SetSel(scriptedit.SelectionStart + tabs, scriptedit.SelectionStart + tabs); + } + } + } + } + + UpdatePositionInfo(); + + // Call tip shown + if (scriptedit.IsCallTipActive) + { + // Should we hide the call tip? + if (curfunctionname.Length == 0) + { + // Hide the call tip + scriptedit.CallTipCancel(); + } + else + { + // Update the call tip + showcalltip = true; + } + } + // No call tip + else + { + // Should we show a call tip? + showcalltip = (curfunctionname.Length > 0) && !scriptedit.IsAutoCActive; + } + + // Show or update call tip + if (showcalltip) + { + string functiondef = scriptconfig.GetFunctionDefinition(curfunctionname); + if (functiondef != null) + { + // Determine the range to highlight + int argsopenpos = functiondef.IndexOf(scriptconfig.FunctionOpen); + int argsclosepos = functiondef.LastIndexOf(scriptconfig.FunctionClose); + if ((argsopenpos > -1) && (argsclosepos > -1)) + { + string argsstr = functiondef.Substring(argsopenpos + 1, argsclosepos - argsopenpos - 1); + string[] args = argsstr.Split(scriptconfig.ArgumentDelimiter[0]); + if ((curargumentindex >= 0) && (curargumentindex < args.Length)) + { + int argoffset = 0; + for (int i = 0; i < curargumentindex; i++) argoffset += args[i].Length + 1; + highlightstart = argsopenpos + argoffset + 1; + highlightend = highlightstart + args[curargumentindex].Length; + } + } + + // Show tip + scriptedit.CallTipShow(curfunctionstartpos, functiondef); + scriptedit.CallTipSetHlt(highlightstart, highlightend); + } + } + } + + #endregion + } +} diff --git a/Source/Core/Controls/ScriptEditorControl.resx b/Source/Core/Controls/ScriptEditorControl.resx new file mode 100644 index 0000000..19dc0dd --- /dev/null +++ b/Source/Core/Controls/ScriptEditorControl.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/Source/Core/Controls/ScriptEditorPanel.Designer.cs b/Source/Core/Controls/ScriptEditorPanel.Designer.cs new file mode 100644 index 0000000..a11cd74 --- /dev/null +++ b/Source/Core/Controls/ScriptEditorPanel.Designer.cs @@ -0,0 +1,404 @@ +namespace CodeImp.DoomBuilder.Controls +{ + partial class ScriptEditorPanel + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(ScriptEditorPanel)); + this.tabs = new System.Windows.Forms.TabControl(); + this.toolbar = new System.Windows.Forms.ToolStrip(); + this.buttonnew = new System.Windows.Forms.ToolStripDropDownButton(); + this.buttonopen = new System.Windows.Forms.ToolStripButton(); + this.buttonsave = new System.Windows.Forms.ToolStripButton(); + this.buttonsaveall = new System.Windows.Forms.ToolStripButton(); + this.toolStripSeparator1 = new System.Windows.Forms.ToolStripSeparator(); + this.buttonundo = new System.Windows.Forms.ToolStripButton(); + this.buttonredo = new System.Windows.Forms.ToolStripButton(); + this.toolStripSeparator2 = new System.Windows.Forms.ToolStripSeparator(); + this.buttoncut = new System.Windows.Forms.ToolStripButton(); + this.buttoncopy = new System.Windows.Forms.ToolStripButton(); + this.buttonpaste = new System.Windows.Forms.ToolStripButton(); + this.toolStripSeparator3 = new System.Windows.Forms.ToolStripSeparator(); + this.buttonscriptconfig = new System.Windows.Forms.ToolStripDropDownButton(); + this.buttoncompile = new System.Windows.Forms.ToolStripButton(); + this.buttonclose = new System.Windows.Forms.ToolStripButton(); + this.buttonkeywordhelp = new System.Windows.Forms.ToolStripButton(); + this.openfile = new System.Windows.Forms.OpenFileDialog(); + this.savefile = new System.Windows.Forms.SaveFileDialog(); + this.splitter = new System.Windows.Forms.SplitContainer(); + this.label1 = new System.Windows.Forms.Label(); + this.errorlist = new System.Windows.Forms.ListView(); + this.colIndex = new System.Windows.Forms.ColumnHeader(); + this.colDescription = new System.Windows.Forms.ColumnHeader(); + this.colFile = new System.Windows.Forms.ColumnHeader(); + this.errorimages = new System.Windows.Forms.ImageList(this.components); + this.toolbar.SuspendLayout(); + this.splitter.Panel1.SuspendLayout(); + this.splitter.Panel2.SuspendLayout(); + this.splitter.SuspendLayout(); + this.SuspendLayout(); + // + // tabs + // + this.tabs.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.tabs.Location = new System.Drawing.Point(3, 8); + this.tabs.Margin = new System.Windows.Forms.Padding(3, 8, 3, 3); + this.tabs.Name = "tabs"; + this.tabs.Padding = new System.Drawing.Point(12, 3); + this.tabs.SelectedIndex = 0; + this.tabs.Size = new System.Drawing.Size(720, 401); + this.tabs.TabIndex = 0; + this.tabs.TabStop = false; + this.tabs.Selecting += new System.Windows.Forms.TabControlCancelEventHandler(this.tabs_Selecting); + this.tabs.MouseUp += new System.Windows.Forms.MouseEventHandler(this.tabs_MouseUp); + // + // toolbar + // + this.toolbar.AllowMerge = false; + this.toolbar.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden; + this.toolbar.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.buttonnew, + this.buttonopen, + this.buttonsave, + this.buttonsaveall, + this.toolStripSeparator1, + this.buttonundo, + this.buttonredo, + this.toolStripSeparator2, + this.buttoncut, + this.buttoncopy, + this.buttonpaste, + this.toolStripSeparator3, + this.buttonscriptconfig, + this.buttoncompile, + this.buttonclose, + this.buttonkeywordhelp}); + this.toolbar.Location = new System.Drawing.Point(0, 0); + this.toolbar.Name = "toolbar"; + this.toolbar.Size = new System.Drawing.Size(726, 25); + this.toolbar.TabIndex = 1; + // + // buttonnew + // + this.buttonnew.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this.buttonnew.Image = global::CodeImp.DoomBuilder.Properties.Resources.NewScript; + this.buttonnew.ImageScaling = System.Windows.Forms.ToolStripItemImageScaling.None; + this.buttonnew.ImageTransparentColor = System.Drawing.Color.Magenta; + this.buttonnew.Margin = new System.Windows.Forms.Padding(6, 1, 0, 2); + this.buttonnew.Name = "buttonnew"; + this.buttonnew.Size = new System.Drawing.Size(29, 22); + this.buttonnew.Text = "New File"; + // + // buttonopen + // + this.buttonopen.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this.buttonopen.Image = global::CodeImp.DoomBuilder.Properties.Resources.OpenScript; + this.buttonopen.ImageScaling = System.Windows.Forms.ToolStripItemImageScaling.None; + this.buttonopen.ImageTransparentColor = System.Drawing.Color.Magenta; + this.buttonopen.Margin = new System.Windows.Forms.Padding(3, 1, 0, 2); + this.buttonopen.Name = "buttonopen"; + this.buttonopen.Size = new System.Drawing.Size(23, 22); + this.buttonopen.Text = "Open File"; + this.buttonopen.Click += new System.EventHandler(this.buttonopen_Click); + // + // buttonsave + // + this.buttonsave.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this.buttonsave.Enabled = false; + this.buttonsave.Image = global::CodeImp.DoomBuilder.Properties.Resources.SaveScript; + this.buttonsave.ImageTransparentColor = System.Drawing.Color.Magenta; + this.buttonsave.Name = "buttonsave"; + this.buttonsave.Size = new System.Drawing.Size(23, 22); + this.buttonsave.Text = "Save File"; + this.buttonsave.Click += new System.EventHandler(this.buttonsave_Click); + // + // buttonsaveall + // + this.buttonsaveall.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this.buttonsaveall.Enabled = false; + this.buttonsaveall.Image = global::CodeImp.DoomBuilder.Properties.Resources.SaveAll; + this.buttonsaveall.ImageTransparentColor = System.Drawing.Color.Magenta; + this.buttonsaveall.Name = "buttonsaveall"; + this.buttonsaveall.Size = new System.Drawing.Size(23, 22); + this.buttonsaveall.Text = "Save All Files"; + this.buttonsaveall.Click += new System.EventHandler(this.buttonsaveall_Click); + // + // toolStripSeparator1 + // + this.toolStripSeparator1.Margin = new System.Windows.Forms.Padding(6, 0, 6, 0); + this.toolStripSeparator1.Name = "toolStripSeparator1"; + this.toolStripSeparator1.Size = new System.Drawing.Size(6, 25); + // + // buttonundo + // + this.buttonundo.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this.buttonundo.Image = global::CodeImp.DoomBuilder.Properties.Resources.Undo; + this.buttonundo.ImageTransparentColor = System.Drawing.Color.Magenta; + this.buttonundo.Name = "buttonundo"; + this.buttonundo.Size = new System.Drawing.Size(23, 22); + this.buttonundo.Text = "Undo"; + this.buttonundo.Click += new System.EventHandler(this.buttonundo_Click); + // + // buttonredo + // + this.buttonredo.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this.buttonredo.Image = global::CodeImp.DoomBuilder.Properties.Resources.Redo; + this.buttonredo.ImageTransparentColor = System.Drawing.Color.Magenta; + this.buttonredo.Name = "buttonredo"; + this.buttonredo.Size = new System.Drawing.Size(23, 22); + this.buttonredo.Text = "Redo"; + this.buttonredo.Click += new System.EventHandler(this.buttonredo_Click); + // + // toolStripSeparator2 + // + this.toolStripSeparator2.Margin = new System.Windows.Forms.Padding(6, 0, 6, 0); + this.toolStripSeparator2.Name = "toolStripSeparator2"; + this.toolStripSeparator2.Size = new System.Drawing.Size(6, 25); + // + // buttoncut + // + this.buttoncut.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this.buttoncut.Image = global::CodeImp.DoomBuilder.Properties.Resources.Cut; + this.buttoncut.ImageTransparentColor = System.Drawing.Color.Magenta; + this.buttoncut.Name = "buttoncut"; + this.buttoncut.Size = new System.Drawing.Size(23, 22); + this.buttoncut.Text = "Cut Selection"; + this.buttoncut.Click += new System.EventHandler(this.buttoncut_Click); + // + // buttoncopy + // + this.buttoncopy.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this.buttoncopy.Image = global::CodeImp.DoomBuilder.Properties.Resources.Copy; + this.buttoncopy.ImageTransparentColor = System.Drawing.Color.Magenta; + this.buttoncopy.Name = "buttoncopy"; + this.buttoncopy.Size = new System.Drawing.Size(23, 22); + this.buttoncopy.Text = "Copy Selection"; + this.buttoncopy.Click += new System.EventHandler(this.buttoncopy_Click); + // + // buttonpaste + // + this.buttonpaste.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this.buttonpaste.Image = global::CodeImp.DoomBuilder.Properties.Resources.Paste; + this.buttonpaste.ImageTransparentColor = System.Drawing.Color.Magenta; + this.buttonpaste.Name = "buttonpaste"; + this.buttonpaste.Size = new System.Drawing.Size(23, 22); + this.buttonpaste.Text = "Paste"; + this.buttonpaste.Click += new System.EventHandler(this.buttonpaste_Click); + // + // toolStripSeparator3 + // + this.toolStripSeparator3.Margin = new System.Windows.Forms.Padding(6, 0, 6, 0); + this.toolStripSeparator3.Name = "toolStripSeparator3"; + this.toolStripSeparator3.Size = new System.Drawing.Size(6, 25); + // + // buttonscriptconfig + // + this.buttonscriptconfig.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this.buttonscriptconfig.Enabled = false; + this.buttonscriptconfig.Image = global::CodeImp.DoomBuilder.Properties.Resources.ScriptPalette; + this.buttonscriptconfig.ImageTransparentColor = System.Drawing.Color.Magenta; + this.buttonscriptconfig.Name = "buttonscriptconfig"; + this.buttonscriptconfig.Size = new System.Drawing.Size(29, 22); + this.buttonscriptconfig.Text = "Change Script Type"; + // + // buttoncompile + // + this.buttoncompile.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this.buttoncompile.Image = global::CodeImp.DoomBuilder.Properties.Resources.ScriptCompile; + this.buttoncompile.ImageTransparentColor = System.Drawing.Color.Magenta; + this.buttoncompile.Margin = new System.Windows.Forms.Padding(3, 1, 0, 2); + this.buttoncompile.Name = "buttoncompile"; + this.buttoncompile.Size = new System.Drawing.Size(23, 22); + this.buttoncompile.Text = "Compile Script"; + this.buttoncompile.Click += new System.EventHandler(this.buttoncompile_Click); + // + // buttonclose + // + this.buttonclose.Alignment = System.Windows.Forms.ToolStripItemAlignment.Right; + this.buttonclose.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this.buttonclose.Image = global::CodeImp.DoomBuilder.Properties.Resources.Close; + this.buttonclose.ImageScaling = System.Windows.Forms.ToolStripItemImageScaling.None; + this.buttonclose.ImageTransparentColor = System.Drawing.Color.Magenta; + this.buttonclose.Name = "buttonclose"; + this.buttonclose.Size = new System.Drawing.Size(23, 22); + this.buttonclose.Text = "Close File"; + this.buttonclose.Click += new System.EventHandler(this.buttonclose_Click); + // + // buttonkeywordhelp + // + this.buttonkeywordhelp.Alignment = System.Windows.Forms.ToolStripItemAlignment.Right; + this.buttonkeywordhelp.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this.buttonkeywordhelp.Image = global::CodeImp.DoomBuilder.Properties.Resources.ScriptHelp; + this.buttonkeywordhelp.ImageTransparentColor = System.Drawing.Color.Magenta; + this.buttonkeywordhelp.Name = "buttonkeywordhelp"; + this.buttonkeywordhelp.Size = new System.Drawing.Size(23, 22); + this.buttonkeywordhelp.Text = "Keyword Help"; + this.buttonkeywordhelp.Click += new System.EventHandler(this.buttonkeywordhelp_Click); + // + // openfile + // + this.openfile.Title = "Open Script"; + // + // savefile + // + this.savefile.Title = "Save Script As"; + // + // splitter + // + this.splitter.Dock = System.Windows.Forms.DockStyle.Fill; + this.splitter.FixedPanel = System.Windows.Forms.FixedPanel.Panel2; + this.splitter.IsSplitterFixed = true; + this.splitter.Location = new System.Drawing.Point(0, 25); + this.splitter.Name = "splitter"; + this.splitter.Orientation = System.Windows.Forms.Orientation.Horizontal; + // + // splitter.Panel1 + // + this.splitter.Panel1.Controls.Add(this.tabs); + // + // splitter.Panel2 + // + this.splitter.Panel2.Controls.Add(this.label1); + this.splitter.Panel2.Controls.Add(this.errorlist); + this.splitter.Size = new System.Drawing.Size(726, 538); + this.splitter.SplitterDistance = 412; + this.splitter.TabIndex = 2; + this.splitter.TabStop = false; + // + // label1 + // + this.label1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.label1.BackColor = System.Drawing.SystemColors.ActiveCaption; + this.label1.Font = new System.Drawing.Font("Tahoma", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label1.ForeColor = System.Drawing.SystemColors.ActiveCaptionText; + this.label1.Location = new System.Drawing.Point(3, 0); + this.label1.Name = "label1"; + this.label1.Padding = new System.Windows.Forms.Padding(1); + this.label1.Size = new System.Drawing.Size(720, 16); + this.label1.TabIndex = 1; + this.label1.Text = "Errors"; + // + // errorlist + // + this.errorlist.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.errorlist.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.colIndex, + this.colDescription, + this.colFile}); + this.errorlist.FullRowSelect = true; + this.errorlist.GridLines = true; + this.errorlist.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.Nonclickable; + this.errorlist.LabelWrap = false; + this.errorlist.Location = new System.Drawing.Point(3, 19); + this.errorlist.MultiSelect = false; + this.errorlist.Name = "errorlist"; + this.errorlist.ShowGroups = false; + this.errorlist.Size = new System.Drawing.Size(720, 100); + this.errorlist.SmallImageList = this.errorimages; + this.errorlist.TabIndex = 0; + this.errorlist.TabStop = false; + this.errorlist.UseCompatibleStateImageBehavior = false; + this.errorlist.View = System.Windows.Forms.View.Details; + this.errorlist.ItemActivate += new System.EventHandler(this.errorlist_ItemActivate); + // + // colIndex + // + this.colIndex.Text = ""; + this.colIndex.Width = 45; + // + // colDescription + // + this.colDescription.Text = "Description"; + this.colDescription.Width = 500; + // + // colFile + // + this.colFile.Text = "File"; + this.colFile.Width = 150; + // + // errorimages + // + this.errorimages.ImageStream = ((System.Windows.Forms.ImageListStreamer)(resources.GetObject("errorimages.ImageStream"))); + this.errorimages.TransparentColor = System.Drawing.Color.Transparent; + this.errorimages.Images.SetKeyName(0, "ScriptError3.png"); + // + // ScriptEditorPanel + // + this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; + this.BackColor = System.Drawing.SystemColors.Control; + this.Controls.Add(this.splitter); + this.Controls.Add(this.toolbar); + this.Name = "ScriptEditorPanel"; + this.Size = new System.Drawing.Size(726, 563); + this.toolbar.ResumeLayout(false); + this.toolbar.PerformLayout(); + this.splitter.Panel1.ResumeLayout(false); + this.splitter.Panel2.ResumeLayout(false); + this.splitter.ResumeLayout(false); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.TabControl tabs; + private System.Windows.Forms.ToolStrip toolbar; + private System.Windows.Forms.ToolStripButton buttonopen; + private System.Windows.Forms.ToolStripDropDownButton buttonnew; + private System.Windows.Forms.OpenFileDialog openfile; + private System.Windows.Forms.SaveFileDialog savefile; + private System.Windows.Forms.ToolStripButton buttonsave; + private System.Windows.Forms.ToolStripButton buttonsaveall; + private System.Windows.Forms.ToolStripSeparator toolStripSeparator1; + private System.Windows.Forms.ToolStripButton buttoncompile; + private System.Windows.Forms.ToolStripButton buttonundo; + private System.Windows.Forms.ToolStripButton buttonredo; + private System.Windows.Forms.ToolStripSeparator toolStripSeparator2; + private System.Windows.Forms.ToolStripButton buttoncut; + private System.Windows.Forms.ToolStripButton buttoncopy; + private System.Windows.Forms.ToolStripButton buttonpaste; + private System.Windows.Forms.ToolStripSeparator toolStripSeparator3; + private System.Windows.Forms.ToolStripDropDownButton buttonscriptconfig; + private System.Windows.Forms.ToolStripButton buttonclose; + private System.Windows.Forms.SplitContainer splitter; + private System.Windows.Forms.ListView errorlist; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.ColumnHeader colIndex; + private System.Windows.Forms.ColumnHeader colDescription; + private System.Windows.Forms.ColumnHeader colFile; + private System.Windows.Forms.ImageList errorimages; + private System.Windows.Forms.ToolStripButton buttonkeywordhelp; + } +} diff --git a/Source/Core/Controls/ScriptEditorPanel.cs b/Source/Core/Controls/ScriptEditorPanel.cs new file mode 100644 index 0000000..86f6800 --- /dev/null +++ b/Source/Core/Controls/ScriptEditorPanel.cs @@ -0,0 +1,807 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Text; +using System.Windows.Forms; +using CodeImp.DoomBuilder.Windows; +using Microsoft.Win32; +using System.Diagnostics; +using CodeImp.DoomBuilder.Data; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Config; +using CodeImp.DoomBuilder.Types; +using CodeImp.DoomBuilder.IO; +using System.Globalization; +using System.IO; +using CodeImp.DoomBuilder.Compilers; + +#endregion + +namespace CodeImp.DoomBuilder.Controls +{ + internal partial class ScriptEditorPanel : UserControl + { + #region ================== Constants + + #endregion + + #region ================== Variables + + private List scriptconfigs; + private List compilererrors; + + // Find/Replace + private ScriptFindReplaceForm findreplaceform; + private FindReplaceOptions findoptions; + + #endregion + + #region ================== Properties + + public ScriptDocumentTab ActiveTab { get { return (tabs.SelectedTab as ScriptDocumentTab); } } + + #endregion + + #region ================== Constructor + + // Constructor + public ScriptEditorPanel() + { + InitializeComponent(); + } + + // This initializes the control + public void Initialize() + { + ToolStripMenuItem item; + + // Make list of script configs + scriptconfigs = new List(General.ScriptConfigs.Values); + scriptconfigs.Add(new ScriptConfiguration()); + scriptconfigs.Sort(); + + // Fill the list of new document types + foreach (ScriptConfiguration cfg in scriptconfigs) + { + // Button for new script menu + item = new ToolStripMenuItem(cfg.Description); + //item.Image = buttonnew.Image; + item.Tag = cfg; + item.Click += new EventHandler(buttonnew_Click); + buttonnew.DropDownItems.Add(item); + + // Button for script type menu + item = new ToolStripMenuItem(cfg.Description); + //item.Image = buttonnew.Image; + item.Tag = cfg; + item.Click += new EventHandler(buttonscriptconfig_Click); + buttonscriptconfig.DropDownItems.Add(item); + } + + // Setup supported extensions + string filterall = ""; + string filterseperate = ""; + foreach (ScriptConfiguration cfg in scriptconfigs) + { + if (cfg.Extensions.Length > 0) + { + string exts = "*." + string.Join(";*.", cfg.Extensions); + if (filterseperate.Length > 0) filterseperate += "|"; + filterseperate += cfg.Description + "|" + exts; + if (filterall.Length > 0) filterall += ";"; + filterall += exts; + } + } + openfile.Filter = "Script files|" + filterall + "|" + filterseperate + "|All files|*.*"; + + // Load the script lumps + foreach (MapLumpInfo maplumpinfo in General.Map.Config.MapLumps.Values) + { + // Is this a script lump? + if (maplumpinfo.script != null) + { + // Load this! + ScriptLumpDocumentTab t = new ScriptLumpDocumentTab(this, maplumpinfo.name, maplumpinfo.script); + tabs.TabPages.Add(t); + } + } + + // Load the files that were previously opened for this map + foreach (String filename in General.Map.Options.ScriptFiles) + { + // Does this file exist? + if (File.Exists(filename)) + { + // Load this! + OpenFile(filename); + } + } + + // Select the first tab + if (tabs.TabPages.Count > 0) tabs.SelectedIndex = 0; + + // If the map has remembered any compile errors, then show them + ShowErrors(General.Map.Errors); + + // Done + UpdateToolbar(true); + } + + // This applies user preferences + public void ApplySettings() + { + // Apply settings + //int panel2size = General.Settings.ReadSetting("scriptspanel.splitter", splitter.ClientRectangle.Height - splitter.SplitterDistance); + //splitter.SplitterDistance = splitter.ClientRectangle.Height - panel2size; + errorlist.Columns[0].Width = General.Settings.ReadSetting("scriptspanel.errorscolumn0width", errorlist.Columns[0].Width); + errorlist.Columns[1].Width = General.Settings.ReadSetting("scriptspanel.errorscolumn1width", errorlist.Columns[1].Width); + errorlist.Columns[2].Width = General.Settings.ReadSetting("scriptspanel.errorscolumn2width", errorlist.Columns[2].Width); + } + + // This saves user preferences + public void SaveSettings() + { + //General.Settings.WriteSetting("scriptspanel.splitter", splitter.ClientRectangle.Height - splitter.SplitterDistance); + General.Settings.WriteSetting("scriptspanel.errorscolumn0width", errorlist.Columns[0].Width); + General.Settings.WriteSetting("scriptspanel.errorscolumn1width", errorlist.Columns[1].Width); + General.Settings.WriteSetting("scriptspanel.errorscolumn2width", errorlist.Columns[2].Width); + } + + #endregion + + #region ================== Methods + + // Find Next + public void FindNext(FindReplaceOptions options) + { + // Save the options + findoptions = options; + FindNext(); + } + + // Find Next with saved options + public void FindNext() + { + if (!string.IsNullOrEmpty(findoptions.FindText) && (ActiveTab != null)) + { + if (!ActiveTab.FindNext(findoptions)) + { + General.MainWindow.DisplayStatus(StatusType.Warning, "Can't find any occurence of \"" + findoptions.FindText + "\"."); + } + } + else + { + General.MessageBeep(MessageBeepType.Default); + } + } + + // Replace if possible + public void Replace(FindReplaceOptions options) + { + if (!string.IsNullOrEmpty(findoptions.FindText) && (options.ReplaceWith != null) && (ActiveTab != null)) + { + if (string.Compare(ActiveTab.GetSelectedText(), options.FindText, !options.CaseSensitive) == 0) + { + // Replace selection + ActiveTab.ReplaceSelection(options.ReplaceWith); + } + } + else + { + General.MessageBeep(MessageBeepType.Default); + } + } + + // Replace all + public void ReplaceAll(FindReplaceOptions options) + { + int replacements = 0; + findoptions = options; + if (!string.IsNullOrEmpty(findoptions.FindText) && (options.ReplaceWith != null) && (ActiveTab != null)) + { + int firstfindpos = -1; + int lastpos = -1; + bool firstreplace = true; + bool wrappedaround = false; + int selectionstart = Math.Min(ActiveTab.SelectionStart, ActiveTab.SelectionEnd); + + // Continue finding and replacing until nothing more found + while (ActiveTab.FindNext(findoptions)) + { + int curpos = Math.Min(ActiveTab.SelectionStart, ActiveTab.SelectionEnd); + if (curpos <= lastpos) + wrappedaround = true; + + if (firstreplace) + { + // Remember where we started replacing + firstfindpos = curpos; + } + else if (wrappedaround) + { + // Make sure we don't go past our start point, or we could be in an endless loop + if (curpos >= firstfindpos) + break; + } + + Replace(findoptions); + replacements++; + firstreplace = false; + + lastpos = curpos; + } + + // Restore selection + ActiveTab.SelectionStart = selectionstart; + ActiveTab.SelectionEnd = selectionstart; + + // Show result + if (replacements == 0) + General.MainWindow.DisplayStatus(StatusType.Warning, "Can't find any occurence of \"" + findoptions.FindText + "\"."); + else + General.MainWindow.DisplayStatus(StatusType.Info, "Replaced " + replacements + " occurences of \"" + findoptions.FindText + "\" with \"" + findoptions.ReplaceWith + "\"."); + } + else + { + General.MessageBeep(MessageBeepType.Default); + } + } + + // This closed the Find & Replace subwindow + public void CloseFindReplace(bool closing) + { + if (findreplaceform != null) + { + if (!closing) findreplaceform.Close(); + findreplaceform = null; + } + } + + // This opens the Find & Replace subwindow + public void OpenFindAndReplace() + { + if (findreplaceform == null) + findreplaceform = new ScriptFindReplaceForm(); + + try + { + if (findreplaceform.Visible) + findreplaceform.Focus(); + else + findreplaceform.Show(this.ParentForm); + + if (ActiveTab.SelectionEnd != ActiveTab.SelectionStart) + findreplaceform.SetFindText(ActiveTab.GetSelectedText()); + } + catch (Exception) + { + // If we can't pop up the find/replace form right now, thats just too bad. + } + } + + // This refreshes all settings + public void RefreshSettings() + { + foreach (ScriptDocumentTab t in tabs.TabPages) + { + t.RefreshSettings(); + } + } + + // This clears all error marks and hides the errors list + public void ClearErrors() + { + // Hide list + splitter.Panel2Collapsed = true; + errorlist.Items.Clear(); + + // Clear marks + foreach (ScriptDocumentTab t in tabs.TabPages) + { + t.ClearMarks(); + } + } + + // This shows the errors panel with the given errors + // Also updates the scripts with markers for the given errors + public void ShowErrors(IEnumerable errors) + { + // Copy list + if (errors != null) + compilererrors = new List(errors); + else + compilererrors = new List(); + + // Fill list + errorlist.BeginUpdate(); + errorlist.Items.Clear(); + int listindex = 1; + foreach (CompilerError e in compilererrors) + { + ListViewItem ei = new ListViewItem(listindex.ToString()); + ei.ImageIndex = 0; + ei.SubItems.Add(e.description); + if (e.filename.StartsWith("?")) + ei.SubItems.Add(e.filename.Replace("?", "") + " (line " + e.linenumber.ToString() + ")"); + else + ei.SubItems.Add(Path.GetFileName(e.filename) + " (line " + e.linenumber.ToString() + ")"); + ei.Tag = e; + errorlist.Items.Add(ei); + listindex++; + } + errorlist.EndUpdate(); + + // Show marks on scripts + foreach (ScriptDocumentTab t in tabs.TabPages) + { + t.MarkScriptErrors(compilererrors); + } + + // Show/hide panel + splitter.Panel2Collapsed = (errorlist.Items.Count == 0); + } + + // This writes all explicitly opened files to the configuration + public void WriteOpenFilesToConfiguration() + { + List files = new List(); + foreach (ScriptDocumentTab t in tabs.TabPages) + { + if (t.ExplicitSave) files.Add(t.Filename); + } + General.Map.Options.ScriptFiles = files; + } + + // This asks to save files and returns the result + public bool AskSaveAll() + { + foreach (ScriptDocumentTab t in tabs.TabPages) + { + if (t.ExplicitSave) + { + if (!CloseScript(t, true)) return false; + } + } + + return true; + } + + // This closes a script and returns true when closed + private bool CloseScript(ScriptDocumentTab t, bool saveonly) + { + if (t.IsChanged) + { + // Ask to save + DialogResult result = MessageBox.Show(this.ParentForm, "Do you want to save changes to " + t.Text + "?", "Close File", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question); + if (result == DialogResult.Yes) + { + // Save file + if (!SaveScript(t)) return false; + } + else if (result == DialogResult.Cancel) + { + // Cancel + return false; + } + } + + if (!saveonly) + { + // Close file + tabs.TabPages.Remove(t); + t.Dispose(); + } + return true; + } + + // This returns true when any of the implicit-save scripts are changed + public bool CheckImplicitChanges() + { + bool changes = false; + foreach (ScriptDocumentTab t in tabs.TabPages) + { + if (!t.ExplicitSave && t.IsChanged) changes = true; + } + return changes; + } + + // This forces the focus to the script editor + public void ForceFocus() + { + ScriptDocumentTab t = (tabs.SelectedTab as ScriptDocumentTab); + tabs.Focus(); + if (t != null) t.Focus(); + } + + // This does an implicit save on all documents that use implicit saving + // Call this to save the lumps before disposing the panel! + public void ImplicitSave() + { + // Save all scripts + foreach (ScriptDocumentTab t in tabs.TabPages) + { + if (!t.ExplicitSave) t.Save(); + } + + UpdateToolbar(false); + } + + // This updates the toolbar for the current status + private void UpdateToolbar(bool focuseditor) + { + int numscriptsopen = tabs.TabPages.Count; + int explicitsavescripts = 0; + ScriptDocumentTab t = null; + + // Any explicit save scripts? + foreach (ScriptDocumentTab dt in tabs.TabPages) + if (dt.ExplicitSave) explicitsavescripts++; + + // Get current script, if any are open + if (numscriptsopen > 0) + t = (tabs.SelectedTab as ScriptDocumentTab); + + // Enable/disable buttons + buttonsave.Enabled = (t != null) && t.ExplicitSave; + buttonsaveall.Enabled = (explicitsavescripts > 0); + buttoncompile.Enabled = (t != null) && (t.Config.Compiler != null); + buttonkeywordhelp.Enabled = (t != null) && !string.IsNullOrEmpty(t.Config.KeywordHelp); + buttonscriptconfig.Enabled = (t != null) && t.IsReconfigurable; + buttonundo.Enabled = (t != null); + buttonredo.Enabled = (t != null); + buttoncopy.Enabled = (t != null); + buttoncut.Enabled = (t != null); + buttonpaste.Enabled = (t != null); + buttonclose.Enabled = (t != null) && t.IsClosable; + + if (t != null) + { + // Check the according script config in menu + foreach (ToolStripMenuItem item in buttonscriptconfig.DropDownItems) + { + ScriptConfiguration config = (item.Tag as ScriptConfiguration); + item.Checked = (config == t.Config); + } + + // Focus to script editor + if (focuseditor) ForceFocus(); + } + } + + // This opens the given file, returns null when failed + public ScriptFileDocumentTab OpenFile(string filename) + { + ScriptConfiguration foundconfig = new ScriptConfiguration(); + + // Find the most suitable script configuration to use + foreach (ScriptConfiguration cfg in scriptconfigs) + { + foreach (string ext in cfg.Extensions) + { + // Use this configuration if the extension matches + if (filename.EndsWith("." + ext, true, CultureInfo.InvariantCulture)) + { + foundconfig = cfg; + break; + } + } + } + + // Create new document + ScriptFileDocumentTab t = new ScriptFileDocumentTab(this, foundconfig); + if (t.Open(filename)) + { + // Mark any errors this script may have + if (compilererrors != null) + t.MarkScriptErrors(compilererrors); + + // Add to tabs + tabs.TabPages.Add(t); + tabs.SelectedTab = t; + + // Done + UpdateToolbar(true); + return t; + } + else + { + // Failed + return null; + } + } + + // This saves the current open script + public void ExplicitSaveCurrentTab() + { + ScriptDocumentTab t = (tabs.SelectedTab as ScriptDocumentTab); + if ((t != null) && t.ExplicitSave) + { + buttonsave_Click(this, EventArgs.Empty); + } + else + { + General.MessageBeep(MessageBeepType.Default); + } + } + + // This opens a script + public void OpenBrowseScript() + { + buttonopen_Click(this, EventArgs.Empty); + } + + #endregion + + #region ================== Events + + // Called when the window that contains this panel closes + public void OnClose() + { + // Close the sub windows now + if (findreplaceform != null) findreplaceform.Dispose(); + } + + // Keyword help requested + private void buttonkeywordhelp_Click(object sender, EventArgs e) + { + // Get script + ScriptDocumentTab t = (tabs.SelectedTab as ScriptDocumentTab); + t.LaunchKeywordHelp(); + } + + // When the user changes the script configuration + private void buttonscriptconfig_Click(object sender, EventArgs e) + { + // Get the tab and new script config + ScriptDocumentTab t = (tabs.SelectedTab as ScriptDocumentTab); + ScriptConfiguration scriptconfig = ((sender as ToolStripMenuItem).Tag as ScriptConfiguration); + + // Change script config + t.ChangeScriptConfig(scriptconfig); + + // Done + UpdateToolbar(true); + } + + // When new script is clicked + private void buttonnew_Click(object sender, EventArgs e) + { + // Get the script config to use + ScriptConfiguration scriptconfig = ((sender as ToolStripMenuItem).Tag as ScriptConfiguration); + + // Create new document + ScriptFileDocumentTab t = new ScriptFileDocumentTab(this, scriptconfig); + tabs.TabPages.Add(t); + tabs.SelectedTab = t; + + // Done + UpdateToolbar(true); + } + + // Open script clicked + private void buttonopen_Click(object sender, EventArgs e) + { + // Show open file dialog + if (openfile.ShowDialog(this.ParentForm) == DialogResult.OK) + { + // TODO: Make multi-select possible + OpenFile(openfile.FileName); + } + } + + // Save script clicked + private void buttonsave_Click(object sender, EventArgs e) + { + // Save the current script + ScriptDocumentTab t = (tabs.SelectedTab as ScriptDocumentTab); + SaveScript(t); + UpdateToolbar(true); + } + + // Save All clicked + private void buttonsaveall_Click(object sender, EventArgs e) + { + // Save all scripts + foreach (ScriptDocumentTab t in tabs.TabPages) + { + // Use explicit save for this script? + if (t.ExplicitSave) + { + if (!SaveScript(t)) break; + } + } + + UpdateToolbar(true); + } + + // This is called by Save and Save All to save a script + // Returns false when cancelled by the user + private bool SaveScript(ScriptDocumentTab t) + { + // Do we have to do a save as? + if (t.IsSaveAsRequired) + { + // Setup save dialog + string scriptfilter = t.Config.Description + "|*." + string.Join(";*.", t.Config.Extensions); + savefile.Filter = scriptfilter + "|All files|*.*"; + if (savefile.ShowDialog(this.ParentForm) == DialogResult.OK) + { + // Save to new filename + t.SaveAs(savefile.FileName); + return true; + } + else + { + // Cancelled + return false; + } + } + else + { + // Save to same filename + t.Save(); + return true; + } + } + + // A tab is selected + private void tabs_Selecting(object sender, TabControlCancelEventArgs e) + { + UpdateToolbar(true); + } + + // This closes the current file + private void buttonclose_Click(object sender, EventArgs e) + { + ScriptDocumentTab t = (tabs.SelectedTab as ScriptDocumentTab); + CloseScript(t, false); + UpdateToolbar(true); + } + + // Compile Script clicked + private void buttoncompile_Click(object sender, EventArgs e) + { + // First save all implicit scripts to the temporary wad file + ImplicitSave(); + + // Get script + ScriptDocumentTab t = (tabs.SelectedTab as ScriptDocumentTab); + + // Check if it must be saved as a new file + if (t.ExplicitSave && t.IsSaveAsRequired) + { + // Save the script first! + if (MessageBox.Show(this.ParentForm, "You must save your script before you can compile it. Do you want to save your script now?", "Compile Script", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.Yes) + { + if (!SaveScript(t)) return; + } + else + { + return; + } + } + else + { + if (t.ExplicitSave && t.IsChanged) + { + // We can only compile when the script is saved + if (!SaveScript(t)) return; + } + } + + // Compile now + General.MainWindow.DisplayStatus(StatusType.Busy, "Compiling script " + t.Text + "..."); + Cursor.Current = Cursors.WaitCursor; + t.Compile(); + + // Show warning + if ((compilererrors != null) && (compilererrors.Count > 0)) + General.MainWindow.DisplayStatus(StatusType.Warning, compilererrors.Count.ToString() + " errors while compiling " + t.Text + "!"); + else + General.MainWindow.DisplayStatus(StatusType.Info, "Script " + t.Text + " compiled without errors."); + + Cursor.Current = Cursors.Default; + UpdateToolbar(true); + } + + // Undo clicked + private void buttonundo_Click(object sender, EventArgs e) + { + ScriptDocumentTab t = (tabs.SelectedTab as ScriptDocumentTab); + t.Undo(); + UpdateToolbar(true); + } + + // Redo clicked + private void buttonredo_Click(object sender, EventArgs e) + { + ScriptDocumentTab t = (tabs.SelectedTab as ScriptDocumentTab); + t.Redo(); + UpdateToolbar(true); + } + + // Cut clicked + private void buttoncut_Click(object sender, EventArgs e) + { + ScriptDocumentTab t = (tabs.SelectedTab as ScriptDocumentTab); + t.Cut(); + UpdateToolbar(true); + } + + // Copy clicked + private void buttoncopy_Click(object sender, EventArgs e) + { + ScriptDocumentTab t = (tabs.SelectedTab as ScriptDocumentTab); + t.Copy(); + UpdateToolbar(true); + } + + // Paste clicked + private void buttonpaste_Click(object sender, EventArgs e) + { + ScriptDocumentTab t = (tabs.SelectedTab as ScriptDocumentTab); + t.Paste(); + UpdateToolbar(true); + } + + // Mouse released on tabs + private void tabs_MouseUp(object sender, MouseEventArgs e) + { + ForceFocus(); + } + + // User double-clicks and error in the list + private void errorlist_ItemActivate(object sender, EventArgs e) + { + // Anything selection? + if (errorlist.SelectedItems.Count > 0) + { + // Get the compiler error + CompilerError err = (CompilerError)errorlist.SelectedItems[0].Tag; + + // Show the tab with the script that matches + bool foundscript = false; + foreach (ScriptDocumentTab t in tabs.TabPages) + { + if (t.VerifyErrorForScript(err)) + { + tabs.SelectedTab = t; + t.MoveToLine(err.linenumber); + foundscript = true; + break; + } + } + + // If we don't have the script opened, see if we can find the file and open the script + if (!foundscript && File.Exists(err.filename)) + { + ScriptDocumentTab t = OpenFile(err.filename); + if (t != null) t.MoveToLine(err.linenumber); + } + + ForceFocus(); + } + } + + #endregion + } +} diff --git a/Source/Core/Controls/ScriptEditorPanel.resx b/Source/Core/Controls/ScriptEditorPanel.resx new file mode 100644 index 0000000..998d902 --- /dev/null +++ b/Source/Core/Controls/ScriptEditorPanel.resx @@ -0,0 +1,182 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + True + + + True + + + 17, 17 + + + 103, 17 + + + 194, 17 + + + True + + + True + + + True + + + 283, 17 + + + + AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj0yLjAuMC4w + LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAACZTeXN0 + ZW0uV2luZG93cy5Gb3Jtcy5JbWFnZUxpc3RTdHJlYW1lcgEAAAAERGF0YQcCAgAAAAkDAAAADwMAAADI + BQAAAk1TRnQBSQFMAwEBAAEEAQABBAEAARABAAEQAQAE/wEhAQAI/wFCAU0BNgcAATYDAAEoAwABQAMA + ARADAAEBAQABIAYAARD/ACMAAc8B2AHyAf8BYAF4AbkB/wEpAUQBkwH/ASEBNwF9Af8BGgEwAXQB/wEa + ATQBgQH/AU8BZQGkAf8BzwHYAfIB/9wAAYEBlAHUAf8BNAFTAawB/wEiAUcBuAH/ARIBQgHQAf8BCQE/ + AeIB/wEIATwB3AH/AQwBOQG9Af8BDwEwAZYB/wEQASsBgQH/AW0BfwGyAf/UAAGJAZ0B3AH/AUEBYAG7 + Af8BIgFSAd0B/wEPAUoB+gH/AQ0BRwH5Af8BTQF4AfgB/wFNAXgB+AH/AQYBPwHqAf8BBgE8AeQB/wEL + ATUBtgH/ARABKwGBAf8BbQF/AbIB/8wAAc8B2AHyAf8BVgFxAcYB/wE0AWAB4wH/ASEBWAH9Af8BIQFY + Af0B/wFcAYEB/gH/A/4B/wP+Af8BXwGBAfoB/wEGAUAB7gH/AQYBPAHkAf8BCwE1AbYB/wEQASsBgQH/ + Ac8B2AHyAf/IAAGnAbYB5QH/AVMBdgHZAf8BMgFlAv8BMwFnAv8BMgFlAv8BawGLAv8D/gH/A/4B/wFW + AX8B+wH/AQgBRAH4Af8BBgFAAe4B/wEGATwB5AH/AQ8BMAGWAf8BTwFlAaQB/8gAAXYBiQHVAf8BUgF5 + Ae4B/wFEAXMC/wFHAXUC/wFEAXMC/wE/AXAC/wFvAY4C/wFmAYYC/wEhAVgB/QH/ARMBTgH7Af8BCAFE + AfgB/wEGAT8B6gH/AQwBOQG9Af8BGgE0AYEB/8gAAYEBlAHRAf8BVgF/AfsB/wFYAYEC/wFcAYEB/gH/ + AVgBgQL/AWYBhgL/A/4B/wP+Af8BRAFzAv8BHgFWAfsB/wEPAUoB+gH/AQYBQQHzAf8BCAE8AdwB/wEa + ATABdAH/yAABigGbAdQB/wFmAYYB+wH/AWsBiwL/AW8BjgL/AWsBiwL/AZcBsQL/A/4B/wP+Af8BfwGa + Av8BJwFdAf0B/wETAU4B+wH/AQgBRAH4Af8BCQE/AeIB/wEhATcBfQH/yAABiQGdAdwB/wF/AZYB8gH/ + AX8BmgL/AYEBnAL/AX8BmgL/AbEBxAL/A/4B/wP+Af8BogG4Af4B/wEuAWIB/gH/AR4BVgH7Af8BDQFH + AfkB/wESAUIB0AH/ASkBRAGTAf/IAAGnAbYB5QH/AY0BowHlAf8BhwGkAv8BjQGoAv8BhwGkAv8BxQHT + Af4B/wP+Af8D/gH/AbUBxwH+Af8BMgFlAv8BIQFYAf0B/wEPAUoB+gH/ASIBRwG4Af8BagGAAboB/8gA + Ac8B2AHyAf8BlwGnAd0B/wGVAasB8QH/AZcBsQL/AY0BqAL/AakBvgL/A/4B/wP+Af8BgwGhAv8BMwFn + Av8BIQFYAf0B/wEiAVIB3QH/ATQBUwGsAf8BzwHYAfIB/8wAAbQBwgHsAf8BmwGqAd0B/wGVAasB8QH/ + AYcBpAL/AX8BmgL/AWsBiwL/AVgBgQL/AUQBcwL/ATMBZwL/ATQBYAHjAf8BQQFgAbsB/wF7AYwB1AH/ + 1AABtAHCAewB/wGXAacB3QH/AY0BowHlAf8BfwGWAfIB/wFmAYYB+wH/AVYBfwH7Af8BUgF5Ae4B/wFT + AXYB2QH/AVYBcQHGAf8BgQGVAdwB/9wAAcwB2AH+Af8BnQGtAeAB/wGJAZ0B3AH/AYoBmwHUAf8BgQGU + AdEB/wF2AYkB1QH/AYEBlAHRAf8BzAHYAf4B//8A0QABQgFNAT4HAAE+AwABKAMAAUADAAEQAwABAQEA + AQEFAAGAFwAD/wEAAv8GAAHwAQ8GAAHgAQcGAAHAAQMGAAGAAQEGAAGAAQEGAAGAAQEGAAGAAQEGAAGA + AQEGAAGAAQEGAAGAAQEGAAGAAQEGAAHAAQMGAAHgAQcGAAHwAQ8GAAL/BgAL + + + + True + + \ No newline at end of file diff --git a/Source/Core/Controls/ScriptFileDocumentTab.cs b/Source/Core/Controls/ScriptFileDocumentTab.cs new file mode 100644 index 0000000..f4b4c49 --- /dev/null +++ b/Source/Core/Controls/ScriptFileDocumentTab.cs @@ -0,0 +1,233 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Text; +using System.Windows.Forms; +using Microsoft.Win32; +using System.Diagnostics; +using CodeImp.DoomBuilder.Data; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Config; +using CodeImp.DoomBuilder.Types; +using CodeImp.DoomBuilder.IO; +using System.IO; +using CodeImp.DoomBuilder.Compilers; + +#endregion + +namespace CodeImp.DoomBuilder.Controls +{ + internal sealed class ScriptFileDocumentTab : ScriptDocumentTab + { + #region ================== Constants + + #endregion + + #region ================== Variables + + private string filepathname; + + #endregion + + #region ================== Properties + + public override bool IsSaveAsRequired { get { return (filepathname.Length == 0); } } + public override string Filename { get { return filepathname; } } + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + public ScriptFileDocumentTab(ScriptEditorPanel panel, ScriptConfiguration config) : base(panel) + { + string ext = ""; + + // Initialize + this.filepathname = ""; + this.config = config; + editor.SetupStyles(config); + if (config.Extensions.Length > 0) ext = "." + config.Extensions[0]; + SetTitle("Untitled" + ext); + editor.ClearUndoRedo(); + } + + // Disposer + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + } + + #endregion + + #region ================== Methods + + // This compiles the script file + public override void Compile() + { + string inputfile, outputfile; + Compiler compiler; + + // List of errors + List errors = new List(); + + try + { + // Initialize compiler + compiler = config.Compiler.Create(); + } + catch (Exception e) + { + // Fail + errors.Add(new CompilerError("Unable to initialize compiler. " + e.GetType().Name + ": " + e.Message)); + return; + } + + // Copy the source file into the temporary directory + inputfile = Path.Combine(compiler.Location, Path.GetFileName(filepathname)); + File.Copy(filepathname, inputfile); + + // Make random output filename + outputfile = General.MakeTempFilename(compiler.Location, "tmp"); + + // Run compiler + compiler.Parameters = config.Parameters; + compiler.InputFile = Path.GetFileName(inputfile); + compiler.OutputFile = Path.GetFileName(outputfile); + compiler.SourceFile = filepathname; + compiler.WorkingDirectory = Path.GetDirectoryName(inputfile); + if (compiler.Run()) + { + // Fetch errors + foreach (CompilerError e in compiler.Errors) + { + CompilerError newerr = e; + + // If the error's filename equals our temporary file, + // replace it with the original source filename + if (string.Compare(e.filename, inputfile, true) == 0) + newerr.filename = filepathname; + + errors.Add(newerr); + } + } + + // Dispose compiler + compiler.Dispose(); + + // Feed errors to panel + panel.ShowErrors(errors); + } + + // This checks if a script error applies to this script + public override bool VerifyErrorForScript(CompilerError e) + { + return (string.Compare(e.filename, filepathname, true) == 0); + } + + // This saves the document (used for both explicit and implicit) + // Return true when successfully saved + public override bool Save() + { + try + { + // Write the file + File.WriteAllBytes(filepathname, editor.GetText()); + } + catch (Exception e) + { + // Failed + General.ErrorLogger.Add(ErrorType.Error, "Cannot open file '" + filepathname + "' for writing. Make sure the path exists and that the file is not in use by another application."); + General.WriteLogLine(e.GetType().Name + ": " + e.Message); + General.ShowErrorMessage("Unable to open file \"" + filepathname + "\" for writing. Make sure the path exists and that the file is not in use by another application.", MessageBoxButtons.OK); + return false; + } + + // Done + editor.IsChanged = false; + return true; + } + + // This saves the document to a new file + // Return true when successfully saved + public override bool SaveAs(string filename) + { + string oldfilename = filepathname; + filepathname = filename; + if (this.Save()) + { + SetTitle(Path.GetFileName(filepathname)); + return true; + } + else + { + this.filepathname = oldfilename; + return false; + } + } + + // This opens a file and returns true when successful + public bool Open(string filepathname) + { + try + { + // Read the file + editor.SetText(File.ReadAllBytes(filepathname)); + } + catch (Exception e) + { + // Failed + General.ErrorLogger.Add(ErrorType.Error, "Cannot open file '" + filepathname + "' for reading. Make sure the path exists and that the file is not in use by another application."); + General.WriteLogLine(e.GetType().Name + ": " + e.Message); + General.ShowErrorMessage("Unable to open file \"" + filepathname + "\" for reading. Make sure the path exists and that the file is not in use by another application.", MessageBoxButtons.OK); + return false; + } + + // Setup + this.filepathname = filepathname; + SetTitle(Path.GetFileName(filepathname)); + editor.ClearUndoRedo(); + return true; + } + + // This changes the script configurations + public override void ChangeScriptConfig(ScriptConfiguration newconfig) + { + string ext = ""; + + this.config = newconfig; + editor.SetupStyles(config); + + if (filepathname.Length == 0) + { + if (config.Extensions.Length > 0) ext = "." + config.Extensions[0]; + SetTitle("Untitled" + ext); + } + } + + #endregion + + #region ================== Events + + #endregion + } +} diff --git a/Source/Core/Controls/ScriptLumpDocumentTab.cs b/Source/Core/Controls/ScriptLumpDocumentTab.cs new file mode 100644 index 0000000..ac2a58a --- /dev/null +++ b/Source/Core/Controls/ScriptLumpDocumentTab.cs @@ -0,0 +1,141 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Text; +using System.Windows.Forms; +using Microsoft.Win32; +using System.Diagnostics; +using CodeImp.DoomBuilder.Data; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Config; +using CodeImp.DoomBuilder.Types; +using CodeImp.DoomBuilder.IO; +using System.IO; +using CodeImp.DoomBuilder.Compilers; + +#endregion + +namespace CodeImp.DoomBuilder.Controls +{ + internal sealed class ScriptLumpDocumentTab : ScriptDocumentTab + { + #region ================== Constants + + #endregion + + #region ================== Variables + + private string lumpname; + private bool ismapheader; + + #endregion + + #region ================== Properties + + public override bool ExplicitSave { get { return false; } } + public override bool IsSaveAsRequired { get { return false; } } + public override bool IsClosable { get { return false; } } + public override bool IsReconfigurable { get { return false; } } + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + public ScriptLumpDocumentTab(ScriptEditorPanel panel, string lumpname, ScriptConfiguration config) : base(panel) + { + // Initialize + if (lumpname == MapManager.CONFIG_MAP_HEADER) + { + this.lumpname = MapManager.TEMP_MAP_HEADER; + this.ismapheader = true; + } + else + { + this.lumpname = lumpname; + this.ismapheader = false; + } + + this.config = config; + editor.SetupStyles(config); + + // Load the lump data + MemoryStream stream = General.Map.GetLumpData(this.lumpname); + if (stream != null) + { + editor.SetText(stream.ToArray()); + editor.ClearUndoRedo(); + } + + // Done + if (ismapheader) + SetTitle(General.Map.Options.CurrentName); + else + SetTitle(this.lumpname.ToUpper()); + } + + // Disposer + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + } + + #endregion + + #region ================== Methods + + // Compile script + public override void Compile() + { + // Compile + if (ismapheader) + General.Map.CompileLump(MapManager.CONFIG_MAP_HEADER, true); + else + General.Map.CompileLump(lumpname, true); + + // Feed errors to panel + panel.ShowErrors(General.Map.Errors); + } + + // Implicit save + public override bool Save() + { + // Store the lump data + MemoryStream stream = new MemoryStream(editor.GetText()); + General.Map.SetLumpData(lumpname, stream); + editor.IsChanged = false; + return true; + } + + // This checks if a script error applies to this script + public override bool VerifyErrorForScript(CompilerError e) + { + return (string.Compare(e.filename, "?" + lumpname, true) == 0); + } + + #endregion + + #region ================== Events + + #endregion + } +} diff --git a/Source/Core/Controls/SectorInfoPanel.Designer.cs b/Source/Core/Controls/SectorInfoPanel.Designer.cs new file mode 100644 index 0000000..6f41fc6 --- /dev/null +++ b/Source/Core/Controls/SectorInfoPanel.Designer.cs @@ -0,0 +1,426 @@ +namespace CodeImp.DoomBuilder.Controls +{ + partial class SectorInfoPanel + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.Windows.Forms.Label label5; + System.Windows.Forms.Label label4; + System.Windows.Forms.Label label3; + System.Windows.Forms.Label label2; + System.Windows.Forms.Label label1; + this.brightLabel = new System.Windows.Forms.Label(); + this.sectorinfo = new System.Windows.Forms.GroupBox(); + this.brightness = new System.Windows.Forms.Label(); + this.height = new System.Windows.Forms.Label(); + this.tag = new System.Windows.Forms.Label(); + this.floor = new System.Windows.Forms.Label(); + this.ceiling = new System.Windows.Forms.Label(); + this.effect = new System.Windows.Forms.Label(); + this.ceilingpanel = new System.Windows.Forms.GroupBox(); + this.ceilingname = new System.Windows.Forms.Label(); + this.ceilingtex = new System.Windows.Forms.Panel(); + this.floorpanel = new System.Windows.Forms.GroupBox(); + this.floorname = new System.Windows.Forms.Label(); + this.floortex = new System.Windows.Forms.Panel(); + this.lightInfo = new System.Windows.Forms.GroupBox(); + this.ceilingcolor = new System.Windows.Forms.Panel(); + this.label6 = new System.Windows.Forms.Label(); + this.label7 = new System.Windows.Forms.Label(); + this.floorcolor = new System.Windows.Forms.Panel(); + this.label8 = new System.Windows.Forms.Label(); + this.uppercolor = new System.Windows.Forms.Panel(); + this.label9 = new System.Windows.Forms.Label(); + this.lowercolor = new System.Windows.Forms.Panel(); + this.label10 = new System.Windows.Forms.Label(); + this.thingcolor = new System.Windows.Forms.Panel(); + label5 = new System.Windows.Forms.Label(); + label4 = new System.Windows.Forms.Label(); + label3 = new System.Windows.Forms.Label(); + label2 = new System.Windows.Forms.Label(); + label1 = new System.Windows.Forms.Label(); + this.sectorinfo.SuspendLayout(); + this.ceilingpanel.SuspendLayout(); + this.floorpanel.SuspendLayout(); + this.lightInfo.SuspendLayout(); + this.SuspendLayout(); + // + // brightLabel + // + this.brightLabel.AutoSize = true; + this.brightLabel.Location = new System.Drawing.Point(111, 77); + this.brightLabel.Name = "brightLabel"; + this.brightLabel.Size = new System.Drawing.Size(62, 14); + this.brightLabel.TabIndex = 14; + this.brightLabel.Text = "Brightness:"; + // + // label5 + // + label5.AutoSize = true; + label5.Location = new System.Drawing.Point(133, 58); + label5.Name = "label5"; + label5.Size = new System.Drawing.Size(40, 14); + label5.TabIndex = 8; + label5.Text = "Height:"; + // + // label4 + // + label4.AutoSize = true; + label4.Location = new System.Drawing.Point(24, 77); + label4.Name = "label4"; + label4.Size = new System.Drawing.Size(28, 14); + label4.TabIndex = 4; + label4.Text = "Tag:"; + // + // label3 + // + label3.AutoSize = true; + label3.Location = new System.Drawing.Point(18, 58); + label3.Name = "label3"; + label3.Size = new System.Drawing.Size(34, 14); + label3.TabIndex = 3; + label3.Text = "Floor:"; + // + // label2 + // + label2.AutoSize = true; + label2.Location = new System.Drawing.Point(11, 39); + label2.Name = "label2"; + label2.Size = new System.Drawing.Size(41, 14); + label2.TabIndex = 2; + label2.Text = "Ceiling:"; + // + // label1 + // + label1.AutoSize = true; + label1.Location = new System.Drawing.Point(13, 19); + label1.Name = "label1"; + label1.Size = new System.Drawing.Size(39, 14); + label1.TabIndex = 0; + label1.Text = "Effect:"; + // + // sectorinfo + // + this.sectorinfo.Controls.Add(this.brightness); + this.sectorinfo.Controls.Add(this.brightLabel); + this.sectorinfo.Controls.Add(this.height); + this.sectorinfo.Controls.Add(label5); + this.sectorinfo.Controls.Add(this.tag); + this.sectorinfo.Controls.Add(this.floor); + this.sectorinfo.Controls.Add(this.ceiling); + this.sectorinfo.Controls.Add(label4); + this.sectorinfo.Controls.Add(label3); + this.sectorinfo.Controls.Add(label2); + this.sectorinfo.Controls.Add(this.effect); + this.sectorinfo.Controls.Add(label1); + this.sectorinfo.Location = new System.Drawing.Point(0, 0); + this.sectorinfo.Name = "sectorinfo"; + this.sectorinfo.Size = new System.Drawing.Size(230, 100); + this.sectorinfo.TabIndex = 2; + this.sectorinfo.TabStop = false; + this.sectorinfo.Text = " Sector "; + // + // brightness + // + this.brightness.AutoSize = true; + this.brightness.Location = new System.Drawing.Point(177, 77); + this.brightness.Name = "brightness"; + this.brightness.Size = new System.Drawing.Size(13, 14); + this.brightness.TabIndex = 17; + this.brightness.Text = "0"; + // + // height + // + this.height.AutoSize = true; + this.height.Location = new System.Drawing.Point(177, 58); + this.height.Name = "height"; + this.height.Size = new System.Drawing.Size(13, 14); + this.height.TabIndex = 11; + this.height.Text = "0"; + // + // tag + // + this.tag.AutoSize = true; + this.tag.Location = new System.Drawing.Point(55, 77); + this.tag.Name = "tag"; + this.tag.Size = new System.Drawing.Size(13, 14); + this.tag.TabIndex = 7; + this.tag.Text = "0"; + // + // floor + // + this.floor.AutoSize = true; + this.floor.Location = new System.Drawing.Point(55, 58); + this.floor.Name = "floor"; + this.floor.Size = new System.Drawing.Size(25, 14); + this.floor.TabIndex = 6; + this.floor.Text = "360"; + // + // ceiling + // + this.ceiling.AutoSize = true; + this.ceiling.Location = new System.Drawing.Point(55, 39); + this.ceiling.Name = "ceiling"; + this.ceiling.Size = new System.Drawing.Size(31, 14); + this.ceiling.TabIndex = 5; + this.ceiling.Text = "1024"; + // + // effect + // + this.effect.AutoSize = true; + this.effect.Location = new System.Drawing.Point(55, 19); + this.effect.Name = "effect"; + this.effect.Size = new System.Drawing.Size(123, 14); + this.effect.TabIndex = 1; + this.effect.Text = "0 - Whacky Pool of Fluid"; + // + // ceilingpanel + // + this.ceilingpanel.Controls.Add(this.ceilingname); + this.ceilingpanel.Controls.Add(this.ceilingtex); + this.ceilingpanel.Location = new System.Drawing.Point(349, 0); + this.ceilingpanel.Name = "ceilingpanel"; + this.ceilingpanel.Size = new System.Drawing.Size(107, 100); + this.ceilingpanel.TabIndex = 3; + this.ceilingpanel.TabStop = false; + this.ceilingpanel.Text = " Ceiling "; + // + // ceilingname + // + this.ceilingname.Location = new System.Drawing.Point(11, 80); + this.ceilingname.Name = "ceilingname"; + this.ceilingname.Size = new System.Drawing.Size(84, 13); + this.ceilingname.TabIndex = 1; + this.ceilingname.Text = "BROWNHUG"; + this.ceilingname.TextAlign = System.Drawing.ContentAlignment.TopCenter; + // + // ceilingtex + // + this.ceilingtex.BackColor = System.Drawing.SystemColors.AppWorkspace; + this.ceilingtex.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Zoom; + this.ceilingtex.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D; + this.ceilingtex.Location = new System.Drawing.Point(19, 19); + this.ceilingtex.Name = "ceilingtex"; + this.ceilingtex.Size = new System.Drawing.Size(68, 60); + this.ceilingtex.TabIndex = 0; + // + // floorpanel + // + this.floorpanel.Controls.Add(this.floorname); + this.floorpanel.Controls.Add(this.floortex); + this.floorpanel.Location = new System.Drawing.Point(236, 0); + this.floorpanel.Name = "floorpanel"; + this.floorpanel.Size = new System.Drawing.Size(107, 100); + this.floorpanel.TabIndex = 4; + this.floorpanel.TabStop = false; + this.floorpanel.Text = " Floor "; + // + // floorname + // + this.floorname.Location = new System.Drawing.Point(11, 80); + this.floorname.Name = "floorname"; + this.floorname.Size = new System.Drawing.Size(84, 13); + this.floorname.TabIndex = 1; + this.floorname.Text = "BROWNHUG"; + this.floorname.TextAlign = System.Drawing.ContentAlignment.TopCenter; + // + // floortex + // + this.floortex.BackColor = System.Drawing.SystemColors.AppWorkspace; + this.floortex.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Zoom; + this.floortex.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D; + this.floortex.Location = new System.Drawing.Point(19, 19); + this.floortex.Name = "floortex"; + this.floortex.Size = new System.Drawing.Size(68, 60); + this.floortex.TabIndex = 0; + // + // lightInfo + // + this.lightInfo.Controls.Add(this.label10); + this.lightInfo.Controls.Add(this.thingcolor); + this.lightInfo.Controls.Add(this.label9); + this.lightInfo.Controls.Add(this.lowercolor); + this.lightInfo.Controls.Add(this.label8); + this.lightInfo.Controls.Add(this.uppercolor); + this.lightInfo.Controls.Add(this.label7); + this.lightInfo.Controls.Add(this.floorcolor); + this.lightInfo.Controls.Add(this.label6); + this.lightInfo.Controls.Add(this.ceilingcolor); + this.lightInfo.Location = new System.Drawing.Point(462, 3); + this.lightInfo.Name = "lightInfo"; + this.lightInfo.Size = new System.Drawing.Size(228, 96); + this.lightInfo.TabIndex = 5; + this.lightInfo.TabStop = false; + this.lightInfo.Text = "Lights"; + // + // ceilingcolor + // + this.ceilingcolor.BackColor = System.Drawing.SystemColors.Window; + this.ceilingcolor.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Zoom; + this.ceilingcolor.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D; + this.ceilingcolor.Location = new System.Drawing.Point(74, 16); + this.ceilingcolor.Name = "ceilingcolor"; + this.ceilingcolor.Size = new System.Drawing.Size(38, 14); + this.ceilingcolor.TabIndex = 0; + // + // label6 + // + this.label6.AutoSize = true; + this.label6.Location = new System.Drawing.Point(10, 16); + this.label6.Name = "label6"; + this.label6.Size = new System.Drawing.Size(41, 14); + this.label6.TabIndex = 5; + this.label6.Text = "Ceiling:"; + // + // label7 + // + this.label7.AutoSize = true; + this.label7.Location = new System.Drawing.Point(10, 36); + this.label7.Name = "label7"; + this.label7.Size = new System.Drawing.Size(34, 14); + this.label7.TabIndex = 7; + this.label7.Text = "Floor:"; + // + // floorcolor + // + this.floorcolor.BackColor = System.Drawing.SystemColors.Window; + this.floorcolor.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Zoom; + this.floorcolor.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D; + this.floorcolor.Location = new System.Drawing.Point(74, 36); + this.floorcolor.Name = "floorcolor"; + this.floorcolor.Size = new System.Drawing.Size(38, 14); + this.floorcolor.TabIndex = 6; + // + // label8 + // + this.label8.AutoSize = true; + this.label8.Location = new System.Drawing.Point(10, 56); + this.label8.Name = "label8"; + this.label8.Size = new System.Drawing.Size(62, 14); + this.label8.TabIndex = 9; + this.label8.Text = "Upper Wall:"; + // + // uppercolor + // + this.uppercolor.BackColor = System.Drawing.SystemColors.Window; + this.uppercolor.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Zoom; + this.uppercolor.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D; + this.uppercolor.Location = new System.Drawing.Point(74, 56); + this.uppercolor.Name = "uppercolor"; + this.uppercolor.Size = new System.Drawing.Size(38, 14); + this.uppercolor.TabIndex = 8; + // + // label9 + // + this.label9.AutoSize = true; + this.label9.Location = new System.Drawing.Point(10, 74); + this.label9.Name = "label9"; + this.label9.Size = new System.Drawing.Size(65, 14); + this.label9.TabIndex = 11; + this.label9.Text = "Lower Wall:"; + // + // lowercolor + // + this.lowercolor.BackColor = System.Drawing.SystemColors.Window; + this.lowercolor.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Zoom; + this.lowercolor.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D; + this.lowercolor.Location = new System.Drawing.Point(74, 74); + this.lowercolor.Name = "lowercolor"; + this.lowercolor.Size = new System.Drawing.Size(38, 14); + this.lowercolor.TabIndex = 10; + // + // label10 + // + this.label10.AutoSize = true; + this.label10.Location = new System.Drawing.Point(125, 16); + this.label10.Name = "label10"; + this.label10.Size = new System.Drawing.Size(36, 14); + this.label10.TabIndex = 13; + this.label10.Text = "Thing:"; + // + // thingcolor + // + this.thingcolor.BackColor = System.Drawing.SystemColors.Window; + this.thingcolor.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Zoom; + this.thingcolor.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D; + this.thingcolor.Location = new System.Drawing.Point(167, 16); + this.thingcolor.Name = "thingcolor"; + this.thingcolor.Size = new System.Drawing.Size(38, 14); + this.thingcolor.TabIndex = 12; + // + // SectorInfoPanel + // + this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; + this.Controls.Add(this.lightInfo); + this.Controls.Add(this.floorpanel); + this.Controls.Add(this.ceilingpanel); + this.Controls.Add(this.sectorinfo); + this.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.MaximumSize = new System.Drawing.Size(10000, 100); + this.MinimumSize = new System.Drawing.Size(100, 100); + this.Name = "SectorInfoPanel"; + this.Size = new System.Drawing.Size(690, 100); + this.sectorinfo.ResumeLayout(false); + this.sectorinfo.PerformLayout(); + this.ceilingpanel.ResumeLayout(false); + this.floorpanel.ResumeLayout(false); + this.lightInfo.ResumeLayout(false); + this.lightInfo.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.Label brightness; + private System.Windows.Forms.Label height; + private System.Windows.Forms.Label tag; + private System.Windows.Forms.Label floor; + private System.Windows.Forms.Label ceiling; + private System.Windows.Forms.Label effect; + private System.Windows.Forms.GroupBox ceilingpanel; + private System.Windows.Forms.Label ceilingname; + private System.Windows.Forms.Panel ceilingtex; + private System.Windows.Forms.GroupBox floorpanel; + private System.Windows.Forms.Label floorname; + private System.Windows.Forms.Panel floortex; + private System.Windows.Forms.GroupBox sectorinfo; + private System.Windows.Forms.Label brightLabel; + private System.Windows.Forms.GroupBox lightInfo; + private System.Windows.Forms.Label label8; + private System.Windows.Forms.Panel uppercolor; + private System.Windows.Forms.Label label7; + private System.Windows.Forms.Panel floorcolor; + private System.Windows.Forms.Label label6; + private System.Windows.Forms.Panel ceilingcolor; + private System.Windows.Forms.Label label10; + private System.Windows.Forms.Panel thingcolor; + private System.Windows.Forms.Label label9; + private System.Windows.Forms.Panel lowercolor; + } +} diff --git a/Source/Core/Controls/SectorInfoPanel.cs b/Source/Core/Controls/SectorInfoPanel.cs new file mode 100644 index 0000000..3589b7e --- /dev/null +++ b/Source/Core/Controls/SectorInfoPanel.cs @@ -0,0 +1,105 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Text; +using System.Windows.Forms; +using Microsoft.Win32; +using System.Diagnostics; +using CodeImp.DoomBuilder.Data; +using CodeImp.DoomBuilder.Map; + +#endregion + +namespace CodeImp.DoomBuilder.Controls +{ + internal partial class SectorInfoPanel : UserControl + { + // Constructor + public SectorInfoPanel() + { + // Initialize + InitializeComponent(); + } + + // This shows the info + public void ShowInfo(Sector s) + { + string effectinfo = ""; + int sheight = s.CeilHeight - s.FloorHeight; + + // villsa + if (General.Map.FormatInterface.InDoom64Mode) + { + brightness.Hide(); + brightLabel.Hide(); + } + else + lightInfo.Hide(); + + // Lookup effect description in config + if (General.Map.Config.SectorEffects.ContainsKey(s.Effect)) + effectinfo = General.Map.Config.SectorEffects[s.Effect].ToString(); + else if (s.Effect == 0) + effectinfo = s.Effect.ToString() + " - Normal"; + else + effectinfo = s.Effect.ToString() + " - Unknown"; + + // Sector info + sectorinfo.Text = " Sector " + s.Index + " "; + effect.Text = effectinfo; + ceiling.Text = s.CeilHeight.ToString(); + floor.Text = s.FloorHeight.ToString(); + tag.Text = s.Tag.ToString(); + height.Text = sheight.ToString(); + brightness.Text = s.Brightness.ToString(); + floorname.Text = s.FloorTexture; + ceilingname.Text = s.CeilTexture; + General.DisplayZoomedImage(floortex, General.Map.Data.GetFlatImage(s.FloorTexture).GetPreview()); + General.DisplayZoomedImage(ceilingtex, General.Map.Data.GetFlatImage(s.CeilTexture).GetPreview()); + + //villsa + ceilingcolor.BackColor = s.CeilColor.color.ToColor(); + floorcolor.BackColor = s.FloorColor.color.ToColor(); + thingcolor.BackColor = s.ThingColor.color.ToColor(); + uppercolor.BackColor = s.TopColor.color.ToColor(); + lowercolor.BackColor = s.LowerColor.color.ToColor(); + + // Show the whole thing + this.Show(); + this.Update(); + } + + // When visible changed + protected override void OnVisibleChanged(EventArgs e) + { + // Hiding panels + if (!this.Visible) + { + floortex.BackgroundImage = null; + ceilingtex.BackgroundImage = null; + } + + // Call base + base.OnVisibleChanged(e); + } + } +} diff --git a/Source/Core/Controls/SectorInfoPanel.resx b/Source/Core/Controls/SectorInfoPanel.resx new file mode 100644 index 0000000..578540a --- /dev/null +++ b/Source/Core/Controls/SectorInfoPanel.resx @@ -0,0 +1,225 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + True + + + True + + + False + + + True + + + False + + + True + + + False + + + True + + + False + + + True + + + False + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + diff --git a/Source/Core/Controls/TextureSelectorControl.cs b/Source/Core/Controls/TextureSelectorControl.cs new file mode 100644 index 0000000..a23598d --- /dev/null +++ b/Source/Core/Controls/TextureSelectorControl.cs @@ -0,0 +1,88 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Text; +using System.Windows.Forms; +using Microsoft.Win32; +using System.Diagnostics; +using CodeImp.DoomBuilder.Actions; +using CodeImp.DoomBuilder.Data; +using CodeImp.DoomBuilder.Config; +using CodeImp.DoomBuilder.Rendering; +using SlimDX.Direct3D9; +using System.Drawing.Imaging; +using System.Drawing.Drawing2D; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Windows; + +#endregion + +namespace CodeImp.DoomBuilder.Controls +{ + public class TextureSelectorControl : ImageSelectorControl + { + // Variables + private bool required; + + // Properties + public bool Required { get { return required; } set { required = value; } } + + // Setup + public override void Initialize() + { + base.Initialize(); + + // Fill autocomplete list + name.AutoCompleteCustomSource.AddRange(General.Map.Data.TextureNames.ToArray()); + allowclear = true; + } + + // This finds the image we need for the given texture name + protected override Image FindImage(string imagename) + { + // Check if name is a "none" texture + if ((imagename.Length < 1) || (imagename[0] == '-')) + { + // Determine image to show + if (required) + return CodeImp.DoomBuilder.Properties.Resources.MissingTexture; + else + return null; + } + else + { + // Set the image + return General.Map.Data.GetTextureImage(imagename).GetPreview(); + } + } + + // This browses for a texture + protected override string BrowseImage(string imagename) + { + string result; + + // Browse for texture + result = TextureBrowserForm.Browse(this.ParentForm, imagename); + if (result != null) return result; else return imagename; + } + } +} diff --git a/Source/Core/Controls/ThingBrowserControl.Designer.cs b/Source/Core/Controls/ThingBrowserControl.Designer.cs new file mode 100644 index 0000000..50bb2fd --- /dev/null +++ b/Source/Core/Controls/ThingBrowserControl.Designer.cs @@ -0,0 +1,212 @@ +namespace CodeImp.DoomBuilder.Controls +{ + partial class ThingBrowserControl + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + System.Windows.Forms.TreeNode treeNode1 = new System.Windows.Forms.TreeNode("Monsters"); + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(ThingBrowserControl)); + this.sizecaption = new System.Windows.Forms.Label(); + this.blockingcaption = new System.Windows.Forms.Label(); + this.positioncaption = new System.Windows.Forms.Label(); + this.typecaption = new System.Windows.Forms.Label(); + this.sizelabel = new System.Windows.Forms.Label(); + this.blockinglabel = new System.Windows.Forms.Label(); + this.positionlabel = new System.Windows.Forms.Label(); + this.typelist = new System.Windows.Forms.TreeView(); + this.thingimages = new System.Windows.Forms.ImageList(this.components); + this.infopanel = new System.Windows.Forms.Panel(); + this.typeid = new CodeImp.DoomBuilder.Controls.NumericTextbox(); + this.infopanel.SuspendLayout(); + this.SuspendLayout(); + // + // sizecaption + // + this.sizecaption.AutoSize = true; + this.sizecaption.Location = new System.Drawing.Point(166, 13); + this.sizecaption.Name = "sizecaption"; + this.sizecaption.Size = new System.Drawing.Size(30, 13); + this.sizecaption.TabIndex = 16; + this.sizecaption.Text = "Size:"; + // + // blockingcaption + // + this.blockingcaption.AutoSize = true; + this.blockingcaption.Location = new System.Drawing.Point(145, 42); + this.blockingcaption.Name = "blockingcaption"; + this.blockingcaption.Size = new System.Drawing.Size(51, 13); + this.blockingcaption.TabIndex = 14; + this.blockingcaption.Text = "Blocking:"; + // + // positioncaption + // + this.positioncaption.AutoSize = true; + this.positioncaption.Location = new System.Drawing.Point(-2, 42); + this.positioncaption.Name = "positioncaption"; + this.positioncaption.Size = new System.Drawing.Size(47, 13); + this.positioncaption.TabIndex = 12; + this.positioncaption.Text = "Position:"; + // + // typecaption + // + this.typecaption.AutoSize = true; + this.typecaption.Location = new System.Drawing.Point(-2, 13); + this.typecaption.Name = "typecaption"; + this.typecaption.Size = new System.Drawing.Size(34, 13); + this.typecaption.TabIndex = 10; + this.typecaption.Text = "Type:"; + // + // sizelabel + // + this.sizelabel.AutoSize = true; + this.sizelabel.Location = new System.Drawing.Point(200, 13); + this.sizelabel.Name = "sizelabel"; + this.sizelabel.Size = new System.Drawing.Size(42, 13); + this.sizelabel.TabIndex = 17; + this.sizelabel.Text = "16 x 96"; + // + // blockinglabel + // + this.blockinglabel.AutoSize = true; + this.blockinglabel.Location = new System.Drawing.Point(198, 42); + this.blockinglabel.Name = "blockinglabel"; + this.blockinglabel.Size = new System.Drawing.Size(63, 13); + this.blockinglabel.TabIndex = 15; + this.blockinglabel.Text = "True-Height"; + // + // positionlabel + // + this.positionlabel.AutoSize = true; + this.positionlabel.Location = new System.Drawing.Point(48, 42); + this.positionlabel.Name = "positionlabel"; + this.positionlabel.Size = new System.Drawing.Size(38, 13); + this.positionlabel.TabIndex = 13; + this.positionlabel.Text = "Ceiling"; + // + // typelist + // + this.typelist.HideSelection = false; + this.typelist.ImageIndex = 0; + this.typelist.ImageList = this.thingimages; + this.typelist.Location = new System.Drawing.Point(0, 0); + this.typelist.Margin = new System.Windows.Forms.Padding(8, 8, 9, 8); + this.typelist.Name = "typelist"; + treeNode1.Name = "Node0"; + treeNode1.Text = "Monsters"; + this.typelist.Nodes.AddRange(new System.Windows.Forms.TreeNode[] { + treeNode1}); + this.typelist.SelectedImageIndex = 0; + this.typelist.Size = new System.Drawing.Size(304, 261); + this.typelist.TabIndex = 0; + this.typelist.DoubleClick += new System.EventHandler(this.typelist_DoubleClick); + this.typelist.AfterSelect += new System.Windows.Forms.TreeViewEventHandler(this.typelist_AfterSelect); + // + // thingimages + // + this.thingimages.ImageStream = ((System.Windows.Forms.ImageListStreamer)(resources.GetObject("thingimages.ImageStream"))); + this.thingimages.TransparentColor = System.Drawing.SystemColors.Window; + this.thingimages.Images.SetKeyName(0, "thing00.png"); + this.thingimages.Images.SetKeyName(1, "thing01.png"); + this.thingimages.Images.SetKeyName(2, "thing02.png"); + this.thingimages.Images.SetKeyName(3, "thing03.png"); + this.thingimages.Images.SetKeyName(4, "thing04.png"); + this.thingimages.Images.SetKeyName(5, "thing05.png"); + this.thingimages.Images.SetKeyName(6, "thing06.png"); + this.thingimages.Images.SetKeyName(7, "thing07.png"); + this.thingimages.Images.SetKeyName(8, "thing08.png"); + this.thingimages.Images.SetKeyName(9, "thing09.png"); + this.thingimages.Images.SetKeyName(10, "thing10.png"); + this.thingimages.Images.SetKeyName(11, "thing11.png"); + this.thingimages.Images.SetKeyName(12, "thing12.png"); + this.thingimages.Images.SetKeyName(13, "thing13.png"); + this.thingimages.Images.SetKeyName(14, "thing14.png"); + this.thingimages.Images.SetKeyName(15, "thing15.png"); + this.thingimages.Images.SetKeyName(16, "thing16.png"); + this.thingimages.Images.SetKeyName(17, "thing17.png"); + this.thingimages.Images.SetKeyName(18, "thing18.png"); + this.thingimages.Images.SetKeyName(19, "thing19.png"); + // + // infopanel + // + this.infopanel.Controls.Add(this.sizelabel); + this.infopanel.Controls.Add(this.typecaption); + this.infopanel.Controls.Add(this.sizecaption); + this.infopanel.Controls.Add(this.typeid); + this.infopanel.Controls.Add(this.blockinglabel); + this.infopanel.Controls.Add(this.positioncaption); + this.infopanel.Controls.Add(this.blockingcaption); + this.infopanel.Controls.Add(this.positionlabel); + this.infopanel.Location = new System.Drawing.Point(0, 261); + this.infopanel.Name = "infopanel"; + this.infopanel.Size = new System.Drawing.Size(304, 59); + this.infopanel.TabIndex = 18; + // + // typeid + // + this.typeid.AllowDecimal = false; + this.typeid.AllowNegative = false; + this.typeid.AllowRelative = false; + this.typeid.ImeMode = System.Windows.Forms.ImeMode.Off; + this.typeid.Location = new System.Drawing.Point(41, 10); + this.typeid.Name = "typeid"; + this.typeid.Size = new System.Drawing.Size(68, 20); + this.typeid.TabIndex = 1; + this.typeid.TextChanged += new System.EventHandler(this.typeid_TextChanged); + // + // ThingBrowserControl + // + this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; + this.Controls.Add(this.typelist); + this.Controls.Add(this.infopanel); + this.Name = "ThingBrowserControl"; + this.Size = new System.Drawing.Size(304, 320); + this.Layout += new System.Windows.Forms.LayoutEventHandler(this.ThingBrowserControl_Layout); + this.Resize += new System.EventHandler(this.ThingBrowserControl_Resize); + this.SizeChanged += new System.EventHandler(this.ThingBrowserControl_SizeChanged); + this.infopanel.ResumeLayout(false); + this.infopanel.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.Label sizelabel; + private System.Windows.Forms.Label blockinglabel; + private System.Windows.Forms.Label positionlabel; + private NumericTextbox typeid; + private System.Windows.Forms.TreeView typelist; + private System.Windows.Forms.ImageList thingimages; + private System.Windows.Forms.Panel infopanel; + private System.Windows.Forms.Label sizecaption; + private System.Windows.Forms.Label blockingcaption; + private System.Windows.Forms.Label positioncaption; + private System.Windows.Forms.Label typecaption; + } +} diff --git a/Source/Core/Controls/ThingBrowserControl.cs b/Source/Core/Controls/ThingBrowserControl.cs new file mode 100644 index 0000000..c1770cd --- /dev/null +++ b/Source/Core/Controls/ThingBrowserControl.cs @@ -0,0 +1,277 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Text; +using System.Windows.Forms; +using CodeImp.DoomBuilder.Windows; +using Microsoft.Win32; +using System.Diagnostics; +using CodeImp.DoomBuilder.Data; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Config; +using CodeImp.DoomBuilder.Types; +using CodeImp.DoomBuilder.IO; +using System.Globalization; +using System.IO; +using CodeImp.DoomBuilder.Compilers; + +#endregion + +namespace CodeImp.DoomBuilder.Controls +{ + public partial class ThingBrowserControl : UserControl + { + #region ================== Events + + public delegate void TypeChangedDeletegate(ThingTypeInfo value); + public delegate void TypeDoubleClickDeletegate(); + + public event TypeChangedDeletegate OnTypeChanged; + public event TypeDoubleClickDeletegate OnTypeDoubleClicked; + + #endregion + + #region ================== Variables + + private ICollection things; + private List nodes; + private ThingTypeInfo thinginfo; + private bool doupdatenode; + private bool doupdatetextbox; + + #endregion + + #region ================== Properties + + public string TypeStringValue { get { return typeid.Text; } } + + #endregion + + #region ================== Constructor + + // Constructor + public ThingBrowserControl() + { + InitializeComponent(); + } + + // This sets up the control + public void Setup() + { + // Go for all predefined categories + typelist.Nodes.Clear(); + nodes = new List(); + foreach (ThingCategory tc in General.Map.Data.ThingCategories) + { + // Create category + TreeNode cn = typelist.Nodes.Add(tc.Name, tc.Title); + if ((tc.Color >= 0) && (tc.Color < thingimages.Images.Count)) cn.ImageIndex = tc.Color; + cn.SelectedImageIndex = cn.ImageIndex; + foreach (ThingTypeInfo ti in tc.Things) + { + // Create thing + TreeNode n = cn.Nodes.Add(ti.Title); + if ((ti.Color >= 0) && (ti.Color < thingimages.Images.Count)) n.ImageIndex = ti.Color; + n.SelectedImageIndex = n.ImageIndex; + n.Tag = ti; + nodes.Add(n); + } + } + + doupdatenode = true; + doupdatetextbox = true; + } + + #endregion + + #region ================== Methods + + // Select a type + public void SelectType(int type) + { + // Set type index + typeid.Text = type.ToString(); + typeid_TextChanged(this, EventArgs.Empty); + } + + // Return selected type info + public ThingTypeInfo GetSelectedInfo() + { + return thinginfo; + } + + // This clears the type + public void ClearSelectedType() + { + doupdatenode = false; + + // Clear selection + typelist.SelectedNode = null; + typeid.Text = ""; + + // Collapse nodes + foreach (TreeNode n in nodes) + if (n.Parent.IsExpanded) n.Parent.Collapse(); + + doupdatenode = true; + } + + // Result + public int GetResult(int original) + { + return typeid.GetResult(original); + } + + #endregion + + #region ================== Events + + // List double-clicked + private void typelist_DoubleClick(object sender, EventArgs e) + { + if (typelist.SelectedNode != null) + { + // Node is a child node? + TreeNode n = typelist.SelectedNode; + if ((n.Nodes.Count == 0) && (n.Tag != null) && (n.Tag is ThingTypeInfo)) + { + if ((OnTypeDoubleClicked != null) && (typeid.Text.Length > 0)) OnTypeDoubleClicked(); + } + } + } + + // Thing type selection changed + private void typelist_AfterSelect(object sender, TreeViewEventArgs e) + { + if (doupdatetextbox) + { + // Anything selected? + if (typelist.SelectedNode != null) + { + TreeNode n = typelist.SelectedNode; + + // Node is a child node? + if ((n.Nodes.Count == 0) && (n.Tag != null) && (n.Tag is ThingTypeInfo)) + { + ThingTypeInfo ti = (n.Tag as ThingTypeInfo); + + // Show info + typeid.Text = ti.Index.ToString(); + } + } + } + } + + // Thing type index changed + private void typeid_TextChanged(object sender, EventArgs e) + { + bool knownthing = false; + + // Any text? + if (typeid.Text.Length > 0) + { + // Get the info + thinginfo = General.Map.Data.GetThingInfoEx(typeid.GetResult(0)); + if (thinginfo != null) + { + knownthing = true; + + // Size + sizelabel.Text = (thinginfo.Radius * 2) + " x " + thinginfo.Height; + + // Hangs from ceiling + if (thinginfo.Hangs) positionlabel.Text = "Ceiling"; else positionlabel.Text = "Floor"; + + // Blocking + switch (thinginfo.Blocking) + { + case ThingTypeInfo.THING_BLOCKING_NONE: blockinglabel.Text = "No"; break; + case ThingTypeInfo.THING_BLOCKING_FULL: blockinglabel.Text = "Completely"; break; + case ThingTypeInfo.THING_BLOCKING_HEIGHT: blockinglabel.Text = "True-Height"; break; + default: blockinglabel.Text = "Unknown"; break; + } + } + + if (doupdatenode) + { + doupdatetextbox = false; + int typeindex = typeid.GetResult(0); + typelist.SelectedNode = null; + foreach (TreeNode n in nodes) + { + // Matching node? + if ((n.Tag as ThingTypeInfo).Index == typeindex) + { + // Select this + n.Parent.Expand(); + typelist.SelectedNode = n; + n.EnsureVisible(); + } + } + doupdatetextbox = true; + } + } + else + { + thinginfo = null; + if (doupdatenode) typelist.SelectedNode = null; + } + + // No known thing? + if (!knownthing) + { + sizelabel.Text = "-"; + positionlabel.Text = "-"; + blockinglabel.Text = "-"; + } + + // Raise event + if (OnTypeChanged != null) OnTypeChanged(thinginfo); + } + + // Layout update! + private void ThingBrowserControl_Layout(object sender, LayoutEventArgs e) + { + ThingBrowserControl_SizeChanged(sender, EventArgs.Empty); + } + + private void ThingBrowserControl_Resize(object sender, EventArgs e) + { + ThingBrowserControl_SizeChanged(sender, EventArgs.Empty); + } + + private void ThingBrowserControl_SizeChanged(object sender, EventArgs e) + { + infopanel.Top = this.ClientSize.Height - infopanel.Height; + infopanel.Width = this.ClientSize.Width; + typelist.Width = this.ClientSize.Width; + typelist.Height = infopanel.Top; + + blockingcaption.Left = infopanel.Width / 2; + blockinglabel.Left = blockingcaption.Right + blockingcaption.Margin.Right; + sizecaption.Left = blockingcaption.Right - sizecaption.Width; + sizelabel.Left = sizecaption.Right + sizecaption.Margin.Right; + } + + #endregion + } +} diff --git a/Source/Core/Controls/ThingBrowserControl.resx b/Source/Core/Controls/ThingBrowserControl.resx new file mode 100644 index 0000000..c9c52bf --- /dev/null +++ b/Source/Core/Controls/ThingBrowserControl.resx @@ -0,0 +1,558 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + 17, 17 + + + + AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj0yLjAuMC4w + LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAACZTeXN0 + ZW0uV2luZG93cy5Gb3Jtcy5JbWFnZUxpc3RTdHJlYW1lcgEAAAAERGF0YQcCAgAAAAkDAAAADwMAAABa + XAAAAk1TRnQBSQFMAgEBFAEAARgBAAEEAQABEAEAARABAAT/ASEBAAj/AUIBTQE2BwABNgMAASgDAAFA + AwABYAMAAQEBAAEgBgABYP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wA3AAGUAYsBxQH/ + AYQBfQHEAf8BgQF0AcQB/wGAAXIBwwH/AX4BcAHBAf8BfgFyAbYB/ygAAQUBVwGXAf8BBQFIAX8B/wEF + AUEBcwH/AQUBPwFwAf8BBQE8AWoB/wEFATUBXQH/KAABSwFkAWYB/wE8AVQBVgH/ATYBTAFOAf8BNAFK + AUwB/wEyAUYBSAH/AS4BPAE9Af8oAAEyAVYBZQH/ASYBSAFWAf8BIQFCAU8B/wEgAUEBTgH/AR8BPQFJ + Af8BHwE0ATwB/yAAAbkBtAHSAf8BnwGVAdIB/wGeAZQB0wH/AZwBkgHSAf8BmwGRAdIB/wGZAY4B0gH/ + AZUBigHQAf8BiwGBAcsB/wGAAXMBxQH/AXQBbgGRAf8YAAFYAZQBywH/AQUBaAG1Af8BBQFoAbQB/wEF + AWYBsgH/AQUBZQGvAf8BBQFjAasB/wEFAV8BpAH/AQUBUwGOAf8BBQFBAXMB/wEtAUcBXAH/GAABigGf + AaEB/wFWAXoBfQH/AVUBegF9Af8BVAF5AXwB/wFSAXcBegH/AVABdQF4Af8BTQFwAXMB/wFEAWEBZAH/ + ATUBTAFOAf8BQwJKAf8YAAF7AZQBoQH/ATUBaQF/Af8BNAFqAX8B/wEzAWgBfgH/ATIBZwF8Af8BMAFl + AXoB/wEuAWABdQH/ASkBVAFlAf8BIQFCAU8B/wE8AUUBSgH/FAABvAG3AdgB/wGrAaEB4gH/Aa4BowHo + Af8BsAGkAewB/wGvAaQB6wH/Aa8BpAHqAf8BrwGkAeoB/wGtAaEB6AH/AaoBnwHiAf8BnQGTAdMB/wGD + AXsBxgH/AXMBbQGRAf8QAAFaAZsB1AH/AQUBfQHbAf8BBQGBAeYB/wEFAYIB7QH/AQUBgQHsAf8BBQGB + AeoB/wEFAYEB6gH/AQUBgQHkAf8BBQF8AdkB/wEFAWcBswH/AQUBSAGAAf8BLAFGAVsB/xAAAY0BqAGq + Af8BYAGTAZgB/wFgAZ8BpQH/AWIBpQGrAf8BYgGjAakB/wFhAaIBqAH/AWEBogGoAf8BXwGeAaMB/wFe + AZMBmAH/AVUBeQF9Af8BPAFVAVcB/wFDAUkBSgH/EAABegGcAawB/wEyAYEBoAH/ASsBiQGxAf8BJgGR + Ab0B/wEnAY8BugH/ASgBjQG4Af8BKAGNAbgB/wEqAYgBrwH/ATABgQGgAf8BMwFpAX8B/wElAUkBWAH/ + ATsBRQFJAf8QAAGsAaIB4wH/Aa8BpAHrAf8BtQGqAfAB/wG1AaoB8AH/AbUBqgHwAf8BtQGqAfAB/wGw + AaUB7QH/AbEBpgHvAf8BrQGiAecB/wGXAY0BzwH/AZkBjgHRAf8BggF6AcYB/xAAAQUBfgHeAf8BBQGB + Ae0B/wEFAYkB+gH/AQUBiQH6Af8BBQGJAfoB/wEFAYkB+gH/AQUBgwHwAf8BBQGFAfQB/wEFAYEB5AH/ + AQUBXwGlAf8BBQFjAasB/wEFAUcBfgH/EAABYAGXAZwB/wFiAaQBqgH/AWkBrAGyAf8BaQGsAbIB/wFp + AawBsgH/AWkBrAGyAf8BYwGmAawB/wFkAakBrwH/AWABnQGiAf8BTwFwAXMB/wFRAXQBdwH/ATsBUwFV + Af8QAAExAYIBpAH/AScBjwG8Af8BIgGbAc0B/wEiAZsBzQH/ASIBmwHNAf8BIgGbAc0B/wElAZIBwAH/ + ASIBlgHGAf8BLQGHAa0B/wEwAWEBdAH/ATEBZAF5Af8BJAFIAVcB/wwAAbIBpwHqAf8BtQGrAe0B/wG0 + AagB8QH/AbUBqgHwAf8BtQGqAfAB/wG1AaoB7wH/AbMBqAHrAf8BpgGdAdMB/wGCAXsBwAH/AWUBVgGu + Af8DBQH/AZkBjwHQAf8BlQGKAdAB/wGBAXcBvQH/CAABBQGCAe8B/wEFAYgB+AH/AQUBiQH6Af8BBQGJ + AfoB/wEFAYkB+gH/AQUBiAH6Af8BBQGEAfEB/wEFAW8BwgH/AQUBRAF4Af8BBQEaASwB/wMFAf8BBQFi + AaoB/wEFAV8BpAH/AQUBPgFtAf8IAAFlAaQBqgH/AWoBqQGvAf8BZwGtAbQB/wFpAawBsgH/AWkBrAGy + Af8BaQGrAbEB/wFnAaUBqgH/AV0CgQH/ATkBTgFQAf8BFwIeAf8DBQH/AVEBdAF3Af8BTQFwAXMB/wE1 + AUcBSQH/CAABKgGQAbsB/wEnAZcBxgH/AR4BnAHRAf8BIgGbAc0B/wEiAZsBzQH/ASMBmgHLAf8BKgGR + AbwB/wE7AW8BgQH/ASUBRAFQAf8BEAEaAR4B/wMFAf8BMQFkAXgB/wEuAWABdQH/ASMBPgFIAf8IAAG1 + AaoB8gH/Ab8BtQHzAf8BtwGsAfAB/wG1AaoB8AH/AakBoAHZAf8BiQGBAcQB/wFqAVsBswH/AWQBWwGN + Af8DBQH/AwUB/wFgAVEBqwH/Aa0BogHlAf8BpAGaAdcB/wGIAYAByAH/CAABBQGKAf0B/wEUAZIC/wEF + AYsB/gH/AQUBiQH6Af8BBQF1Ac4B/wEFAU0BgwH/AQUBIgE6Af8BBQEGAQcB/wMFAf8DBQH/AQUBFAEh + Af8BBQGAAeEB/wEFAXABxAH/AQUBTgGFAf8IAAFpAa4BtQH/AXcBtQG7Af8BbAGtAbMB/wFpAawBsgH/ + AWABhgGKAf8BQQFZAVsB/wEdAScBKAH/AwYB/wMFAf8DBQH/ARICFwH/AWABmgGgAf8BWwGBAYMB/wFA + AVsBXQH/CAABHwGeAdIB/wEgAakB4gH/ASMBnQHPAf8BIgGbAc0B/wE5AXgBjAH/ASkBTQFbAf8BEwEi + ASgB/wEFAgYB/wMFAf8DBQH/AQwBFAEXAf8BLgGFAaoB/wE2AXMBhgH/AScBTwFfAf8IAAG9AbMB8wH/ + AcYBvQH1Af8BugGwAfAB/wG1AaoB8AH/AZUBigHKAf8BgAFzAbwB/wFdAU4BpAH/AwUB/wMFAf8DBQH/ + AYQBfgHBAf8BsgGnAfAB/wGpAZ4B4gH/AY4BggHNAf8IAAERAZEC/wEhAZgC/wEJAY0C/wEFAYkB+gH/ + AQUBWgGcAf8BBQE7AWcB/wEFAQ0BFAH/AwUB/wMFAf8DBQH/AQUBRwF9Af8BBQGGAfYB/wEFAXsB1wH/ + AQUBVgGUAf8IAAF0AbQBugH/AYEBugHAAf8BcQGvAbUB/wFpAawBsgH/AUwBaQFrAf8BMgFDAUUB/wEM + AQ4BDwH/AwUB/wMFAf8DBQH/ATwBUgFTAf8BZQGrAbEB/wFeAZIBlwH/AUYBZgFoAf8IAAEgAacB3gH/ + ASoBrgHkAf8BJQGgAdMB/wEiAZsBzQH/ATEBWwFrAf8BIQE7AUUB/wEJAQ0BDwH/AwUB/wMFAf8DBQH/ + AScBRwFTAf8BIQGYAcoB/wEwAYEBnwH/ASsBWAFqAf8IAAHBAbgB9AH/AcoBwgH1Af8BuwGxAfAB/wG1 + AaoB8AH/AbUBqgHvAf8BtQGqAe8B/wFyAWUBtwH/AwUB/wMFAf8BXAFOAZ0B/wGoAZ8B1wH/Aa8BpAHr + Af8BrQGiAeYB/wGRAYUBzgH/CAABGAGUAv8BKAGbAv8BCQGNAv8BBQGJAfoB/wEFAYgB+gH/AQUBiAH5 + Af8BBQEsAUwB/wMFAf8DBQH/AQUBCAEKAf8BBQF0AcsB/wEFAYEB7AH/AQUBgQHiAf8BBQFZAZoB/wgA + AXkBtwG9Af8BgQG9AcIB/wFxAa8BtAH/AWkBrAGyAf8BaQGrAbEB/wFpAasBsQH/ASUBMgE0Af8DBQH/ + AwUB/wEHAggB/wFfAYMBhwH/AWIBowGpAf8BXwGbAaEB/wFJAWkBbAH/CAABIwGrAeMB/wEwAbAB5QH/ + ASYBnwHRAf8BIgGbAc0B/wEjAZoBywH/ASQBmQHJAf8BGQEsATQB/wMFAf8DBQH/AQYCCAH/AToBdgGJ + Af8BJwGPAboB/wEsAYYBrAH/ASwBWwFuAf8IAAHAAbcB9AH/Ac8ByAH2Af8BwAG2AfMB/wG0AakB7AH/ + AbIBqAHqAf8BegFtAboB/wE+ASMBxAH/AYMBfQHCAf8BWQFKAaIB/wFyAWUBtwH/AbUBqgHuAf8BtQGq + AfAB/wGrAaEB4wH/AZEBhgHNAf8IAAEXAZQC/wEyAaAC/wEVAZMC/wEFAYUB9AH/AQUBgwHwAf8BBQE0 + AVsB/wMFAf8BBQFGAXwB/wEFAQgBCwH/AQUBLAFMAf8BBQGIAfgB/wEFAYkB+gH/AQUBfgHdAf8BBQFZ + AZkB/wgAAXgBtgG8Af8BiAHBAcYB/wF4AbUBuwH/AWgBpwGsAf8BZgGkAakB/wEsATwBPgH/AwUB/wE7 + AVEBUwH/AQgCCQH/ASUBMgE0Af8BaQGqAbAB/wFpAawBsgH/AV8BlgGbAf8BSQFoAWsB/wgAASIBqgHj + Af8BOQGzAeYB/wEiAakB4QH/ASkBlAHAAf8BKwGQAboB/wEdATQBPgH/AwUB/wEmAUYBUwH/AQYBCAEJ + Af8BGQEsATQB/wElAZgByAH/ASIBmwHNAf8BLwGBAaQB/wEtAVoBbAH/CAABvAGxAfMB/wHTAcwB9wH/ + AcwBxAH2Af8BuwGyAeoB/wF6AW0BugH/AwUB/wGTAYkByQH/AbcBqwHyAf8BcQFjAbYB/wGVAYoBygH/ + AbYBqwHxAf8BtQGqAfAB/wGrAaEB4gH/AZgBjwHMAf8IAAEOAZAC/wE4AaMC/wEsAZ0C/wEFAYoB/QH/ + AQUBNAFbAf8DBQH/AQUBWAGXAf8BBQGLAv8BBQEqAUgB/wEFAVoBnAH/AQUBiwH+Af8BBQGJAfoB/wEF + AX0B2wH/AQUBXwGkAf8IAAFyAbMBuQH/AY0BwwHIAf8BgwG/AcQB/wFyAagBrQH/ASwBPAE+Af8DBQH/ + AUoBZgFoAf8BawGvAbUB/wEkATABMQH/AUwBaQFrAf8BawGvAbUB/wFpAawBsgH/AV8BlAGYAf8BUAFu + AXEB/wgAASABpQHdAf8BPgG1AeYB/wEzAbEB5QH/ATIBlgHAAf8BHQE0AT4B/wMFAf8BMAFYAWgB/wEf + AZ8B1QH/ARgBKgExAf8BMQFbAWsB/wEgAZ4B0wH/ASIBmwHNAf8BMQGBAaAB/wEzAV8BcQH/DAAB0wHN + AfcB/wHWAdAB9wH/AakBowHMAf8BZAFbAY0B/wGUAYoBygH/AbYBqwHxAf8BtQGqAe4B/wGkAZsB0gH/ + Aa8BpQHkAf8BtQGqAfAB/wG1AaoB8AH/AawBogHkAf8QAAE6AaMC/wE+AaUC/wEFAW4BwAH/AQUBBgEH + Af8BBQFaAZsB/wEFAYoB/QH/AQUBiAH4Af8BBQFsAb0B/wEFAYEB5AH/AQUBiQH6Af8BBQGJAfoB/wEF + AX8B4AH/EAABjgHEAcgB/wGRAcYBygH/AWEBfAF+Af8DBgH/AUwBaAFrAf8BagGtAbQB/wFpAaoBsAH/ + AVsBfgGBAf8BYwGaAZ8B/wFpAawBsgH/AWkBrAGyAf8BYAGZAZ4B/xAAAT8BtQHnAf8BQwG2AecB/wFD + AWoBegH/AQUCBgH/ATABWgFrAf8BIQGdAdAB/wElAZgByAH/AToBbQGBAf8BMgGFAagB/wEiAZsBzQH/ + ASIBmwHNAf8BLwGDAacB/xAAAd0B2AH5Af8B2gHVAfgB/wHCAbsB5gH/AbABqgHVAf8BwQG3AfIB/wG7 + AbIB7gH/AbkBrwHvAf8BtQGqAfAB/wG1AaoB8AH/AbUBqgHwAf8BtQGqAfAB/wG8AbYB3AH/EAABfAHB + Av8BRgGpAv8BCgGOAv8BBQF6AdUB/wEVAZMC/wEIAY0C/wEGAYwC/wEFAYkB+gH/AQUBiQH6Af8BBQGJ + AfoB/wEFAYkB+gH/AVcBnQHcAf8QAAGxAdcB2gH/AZcByQHNAf8BfQGlAakB/wFoAYcBigH/AXkBtAG6 + Af8BcgGtAbIB/wFwAa0BsgH/AWkBrAGyAf8BaQGsAbIB/wFpAawBsgH/AWkBrAGyAf8BiwGvAbIB/xAA + AYEBzwHvAf8BSQG5AegB/wFDAZMBtgH/AUQBeAGIAf8BJgGnAd0B/wErAZ0BzAH/ASgBnAHNAf8BIgGb + Ac0B/wEiAZsBzQH/ASIBmwHNAf8BIgGbAc0B/wFyAaIBtwH/FAAB4QHdAfoB/wHWAdAB+AH/AdMBzAH3 + Af8BzgHHAfYB/wHFAbwB9AH/AbwBsQHyAf8BtwGtAfAB/wGzAagB7gH/AbEBpgHvAf8BvwG4AeEB/xgA + AYEBxQL/AT8BpgL/ATkBowL/ATABnwL/AR4BlwL/AQ0BjwL/AQUBiwH+Af8BBQGGAfYB/wEFAYUB9AH/ + AVoBogHiAf8YAAG1AdkB3AH/AZIBxgHKAf8BjQHEAcgB/wGGAcABxQH/AX4BuQG/Af8BcgGyAbgB/wFt + Aa0BswH/AWcBqQGvAf8BZAGpAbAB/wGNAbYBuQH/GAABgwHRAfAB/wFDAbcB5wH/AT4BtQHmAf8BNwGy + AeUB/wEoAawB5AH/ASIBowHZAf8BJAGcAc4B/wElAZcBxgH/ASEBlgHHAf8BcQGpAcIB/yAAAcMBugH0 + Af8BxwG/AfUB/wHLAcMB9QH/AcUBvAH0Af8BvAGxAfMB/wGxAaYB7wH/KAABHQGWAv8BIwGZAv8BKgGc + Av8BHgGXAv8BDgGQAv8BBQGFAfQB/ygAAX0BuQG+Af8BgQG7AcAB/wGCAb4BwwH/AX4BuQG/Af8BcgGz + AbkB/wFkAakBrwH/KAABJgGsAeQB/wEsAa4B5AH/ATIBsAHlAf8BKAGsAeQB/wEgAaUB3QH/ASIBlgHG + Af//AP8AKgABOwFHAYIB/wErATYBdAH/ASYBMAFrAf8BJQEvAWgB/wEjAS0BYgH/ASUBKwFRAf8oAAGS + AVMBkgH/AYEBQgGBAf8BfQE8AX0B/wF7AToBewH/AXUBOQF1Af8BZQE5AWUB/ygAAQUCkAH/AQUCegH/ + AQUCbwH/AQUCbAH/AQUCZgH/AQUCWQH/KAADpQH/A5EB/wOHAf8DhQH/A4EB/wN6Af8gAAGBAYYBvAH/ + ATwBTQGmAf8BOwFMAacB/wE6AUsBpQH/ATkBSQGjAf8BNwFHAaAB/wE1AUQBmQH/AS8BPAGDAf8BJQEv + AWsB/wE/AUIBVAH/GAABvgGTAb4B/wGwAVoBsAH/AbEBWAGxAf8BsAFWAbAB/wGvAVQBrwH/AawBUgGs + Af8BpgFPAaYB/wGTAUcBkwH/AX0BOwF9Af8BYAFLAWAB/xgAAXwCyQH/AQUCrQH/AQUCrQH/AQUCqwH/ + AQUCqAH/AQUCpAH/AQUCnQH/AQUCiAH/AQUCbgH/AT4CXgH/GAADyQH/A7sB/wO7Af8DuAH/A7YB/wO1 + Af8DrwH/A54B/wOHAf8DbgH/FAABgAGHAcsB/wE9AVQB0wH/AT4BVwHhAf8BPwFZAekB/wE/AVkB5wH/ + AT4BWAHmAf8BPgFYAeYB/wE7AVUB4QH/AToBUgHVAf8BOwFLAaYB/wEqATUBdwH/AT4BQQFTAf8QAAHH + AZYBxwH/AckBbgHJAf8B0wFyAdMB/wHZAXQB2QH/AdgBdQHYAf8B1wFzAdcB/wHXAXMB1wH/AdIBbwHS + Af8BygFsAcoB/wGwAVgBsAH/AYIBQQGCAf8BYAFKAWAB/xAAAX4C0QH/AQUC0QH/AQUC3AH/AQUC5AH/ + AQUC4gH/AQUC4QH/AQUC4QH/AQUC2gH/AQUC0AH/AQUCrAH/AQUCewH/AT0CXQH/EAAD0QH/A9MB/wPa + Af8D3wH/A94B/wPcAf8D3AH/A9kB/wPSAf8DugH/A5IB/wNtAf8QAAE/AVYB1wH/AT8BWQHoAf8BRwFh + AfAB/wFHAWEB8AH/AUcBYQHwAf8BRwFhAfAB/wE/AVoB6wH/AUEBWwHvAf8BPgFXAd4B/wE3AUYBmAH/ + ATgBSAGfAf8BKQE0AXUB/xAAAcsBcAHLAf8B2AF0AdgB/wHgAX4B4AH/AeABfgHgAf8B4AF+AeAB/wHg + AX4B4AH/AdsBdgHbAf8B3gF4Ad4B/wHRAXEB0QH/AaUBUQGlAf8BqwFTAasB/wGBAUABgQH/EAABBQLV + Af8BBQLjAf8BBQLwAf8BBQLwAf8BBQLwAf8BBQLwAf8BBQLmAf8BBQLpAf8BBQLaAf8BBQKeAf8BBQKk + Af8BBQJ5Af8QAAPVAf8D3wH/A+YB/wPmAf8D5gH/A+YB/wPgAf8D4QH/A9kB/wOwAf8DtQH/A5AB/wwA + AUQBXQHlAf8BSgFjAesB/wFDAV4B9AH/AUcBYQHwAf8BRwFhAfAB/wFHAWEB7wH/AUcBXwHlAf8BRAFV + Aa4B/wErATUBbAH/ARIBFQEnAf8DBQH/ATkBSAGdAf8BNQFEAZkB/wEpATEBYQH/CAAB1wF5AdcB/wHc + AX8B3AH/AeIBfAHiAf8B4AF+AeAB/wHgAX4B4AH/Ad8BfgHfAf8B2AF7AdgB/wGyAWcBsgH/AX0BQQF9 + Af8BQAEjAUAB/wMFAf8BqgFTAaoB/wGmAU8BpgH/AXQBPgF0Af8IAAEFAuUB/wEFAu4B/wEFAvAB/wEF + AvAB/wEFAvAB/wEFAu8B/wEFAucB/wEFArkB/wEFAnMB/wEFAioB/wMFAf8BBQKjAf8BBQKdAf8BBQJp + Af8IAAPfAf8D5AH/A+YB/wPmAf8D5gH/A+UB/wPgAf8DwwH/A4sB/wNIAf8DBQH/A7QB/wOvAf8DggH/ + CAABRgFhAfQB/wFbAXMB9QH/AUwBZQHwAf8BRwFhAfAB/wFBAVQBwAH/ATABOwF6Af8BFgEbATUB/wMG + Af8DBQH/AwUB/wEOARABHgH/AT0BVgHcAf8BPQFQAbgB/wEtATkBgAH/CAAB4wF+AeMB/wHmAYsB5gH/ + AeABgQHgAf8B4AF+AeAB/wG7AWwBuwH/AYYBRwGGAf8BTAEoAUwB/wEgARYBIAH/AwUB/wMFAf8BOAEe + ATgB/wHPAXABzwH/AbgBYwG4Af8BiwFEAYsB/wgAAQUC8gH/AQkD/wEFAvQB/wEFAvAB/wEFAsUB/wEF + AoEB/wEFAjgB/wEFAgYB/wMFAf8DBQH/AQUCIAH/AQUC2AH/AQUCvAH/AQUCgQH/CAAD5wH/A/IB/wPn + Af8D5gH/A8kB/wOYAf8DWgH/AwwB/wMFAf8DBQH/AzwB/wPWAf8DwwH/A5kB/wgAAVcBbwH1Af8BagGA + AfYB/wFTAWsB7wH/AUcBYQHwAf8BOAFGAYwB/wEmAS4BXAH/AQoBCwETAf8DBQH/AwUB/wMFAf8BLQE3 + AXAB/wFBAVwB8QH/ATkBUQHUAf8BMAE/AYoB/wgAAeYBhwHmAf8B6QGXAekB/wHhAYMB4QH/AeABfgHg + Af8BmgFRAZoB/wFwATsBcAH/AS4BGQEuAf8DBQH/AwUB/wMFAf8BgQFDAYEB/wHfAXkB3wH/AckBawHJ + Af8BmQFKAZkB/wgAAQUD/wEVA/8BBQL5Af8BBQLwAf8BBQKWAf8BBQJjAf8BBQIUAf8DBQH/AwUB/wMF + Af8BBQJ4Af8BBQLsAf8BBQLPAf8BBQKOAf8IAAPwAf8D+QH/A+sB/wPmAf8DqgH/A4EB/wMqAf8DBQH/ + AwUB/wMFAf8DkAH/A+IB/wPQAf8DpAH/CAABYAF3AfYB/wFzAYIB9wH/AVQBbAHuAf8BRwFhAfAB/wFH + AWEB7wH/AUgBYgHuAf8BHAEiAUUB/wMFAf8DBQH/AgcBCgH/AUIBVQG7Af8BPwFZAecB/wE8AVUB3gH/ + ATEBQAGQAf8IAAHnAY4B5wH/AesBnwHrAf8B4AGDAeAB/wHgAX4B4AH/Ad8BfgHfAf8B3gF+Ad4B/wFa + AS8BWgH/AwUB/wMFAf8BJQEVASUB/wG5AWsBuQH/AdgBdQHYAf8B0AFwAdAB/wGeAUsBngH/CAABDQP/ + ARwD/wEFAvkB/wEFAvAB/wEFAu8B/wEFAu8B/wEFAkkB/wMFAf8DBQH/AQUCCgH/AQUCwgH/AQUC4gH/ + AQUC2QH/AQUCkwH/CAAD9AH/A/0B/wPrAf8D5gH/A+UB/wPlAf8DbAH/AwUB/wMFAf8DFQH/A8gB/wPe + Af8D2AH/A6gB/wgAAV4BdQH1Af8BfgGMAfcB/wFdAXQB9AH/AUgBYQHnAf8BRgFfAeQB/wEhASkBUgH/ + AwUB/wEsATYBbwH/AgcBCwH/ARwBIgFFAf8BSAFiAe0B/wFHAWEB8AH/ATwBVAHYAf8BMwFBAY0B/wgA + AecBjQHnAf8B7QGpAe0B/wHmAYwB5gH/AdoBfQHaAf8B1wF6AdcB/wFmATYBZgH/ASgBDAEoAf8BgQFC + AYEB/wEnARUBJwH/AVoBLwFaAf8B3QF9Ad0B/wHgAX4B4AH/AcwBbgHMAf8BnAFMAZwB/wgAAQsD/wEm + A/8BCgP/AQUC6gH/AQUC5gH/AQUCWAH/AwUB/wEFAncB/wEFAgsB/wEFAkkB/wEFAu4B/wEFAvAB/wEF + AtQB/wEFApMB/wgAA/MB/wQAA/MB/wPiAf8D4AH/A3kB/wMFAf8DjgH/AxgB/wNsAf8D5AH/A+YB/wPU + Af8DqAH/CAABVAFtAfUB/wGBAZIB+AH/AXcBhgH3Af8BWQFvAeIB/wEhASkBUgH/AwUB/wE3AUQBhwH/ + AUgBYwH0Af8BGwEhAUEB/wE4AUYBjAH/AUkBYwHzAf8BRwFhAfAB/wE8AVQB1AH/ATsBSQGTAf8IAAHl + AYQB5QH/Ae4BrwHuAf8B6wGjAesB/wHYAYQB2AH/AWYBNgFmAf8DBQH/AZYBUAGWAf8B4wGAAeMB/wFX + AS4BVwH/AZoBUQGaAf8B4gGAAeIB/wHgAX4B4AH/AckBbgHJAf8BoQFVAaEB/wgAAQUC/gH/ASwD/wEg + A/8BBQLzAf8BBQJYAf8DBQH/AQUCkQH/AQUC9AH/AQUCRgH/AQUClgH/AQUC9AH/AQUC8AH/AQUC0QH/ + AQUCngH/CAAD7gH/CAAD5wH/A3kB/wMFAf8DpgH/A+gB/wNnAf8DqgH/A+cB/wPmAf8D0wH/A68B/wwA + AYIBkwH4Af8BhwGYAfgB/wFRAV0BnwH/AwYB/wE4AUUBiwH/AUgBYgHyAf8BSAFiAe0B/wFEAVQBqgH/ + AUQBWwHYAf8BRwFhAfAB/wFHAWEB8AH/AT0BVQHaAf8QAAHuAbAB7gH/Ae8BtQHvAf8BpwFvAacB/wEg + ARYBIAH/AZkBUQGZAf8B4QF/AeEB/wHdAX0B3QH/AbABZAGwAf8BzQF1Ac0B/wHgAX4B4AH/AeABfgHg + Af8BzQFwAc0B/xAAAS0D/wExA/8BBQK4Af8BBQIGAf8BBQKVAf8BBQLyAf8BBQLuAf8BBQK1Af8BBQLa + Af8BBQLwAf8BBQLwAf8BBQLXAf8YAAPCAf8DDAH/A6kB/wPnAf8D5AH/A8EB/wPZAf8D5gH/A+YB/wPV + Af8QAAGjAbEB+gH/AY8BnwH5Af8BbwF/AdUB/wFUAWMBtgH/AWABdwHxAf8BVwFuAeoB/wFTAWoB7AH/ + AUcBYQHwAf8BRwFhAfAB/wFHAWEB8AH/AUcBYQHwAf8BfQGGAdQB/xAAAfIBwQHyAf8B8QG8AfEB/wHR + AZMB0QH/AbYBewG2Af8B5AGOAeQB/wHeAYUB3gH/Ad4BgQHeAf8B4AF+AeAB/wHgAX4B4AH/AeABfgHg + Af8B4AF+AeAB/wHOAZYBzgH/EAABmAP/ATkD/wEFAvkB/wEFAswB/wEKA/8BBQL4Af8BBQL2Af8BBQLw + Af8BBQLwAf8BBQLwAf8BBQLwAf8BegLUAf8QAAP8Af8EAAPrAf8DzwH/A/MB/wPrAf8D6gH/A+YB/wPm + Af8D5gH/A+YB/wPVAf8UAAGsAbcB+wH/AYgBmQH4Af8BgQGTAfgB/wF7AYkB9wH/AWcBfQH2Af8BVAFt + AfMB/wFNAWYB7wH/AUUBXwHtAf8BQAFbAfAB/wF+AYgB3QH/GAAB9AHJAfQB/wHvAbYB7wH/Ae4BrwHu + Af8B7AGmAewB/wHoAZUB6AH/AeQBhAHkAf8B4AGBAeAB/wHdAXsB3QH/Ad4BdwHeAf8B1AGZAdQB/xgA + AZ4D/wEyA/8BLAP/ASQD/wETA/8BBQL8Af8BBQL0Af8BBQLsAf8BBQLpAf8BfgLaAf8oAAP3Af8D7QH/ + A+cB/wPiAf8D4QH/A9kB/yAAAWUBewH2Af8BbQGBAfYB/wF0AYQB9wH/AWcBfQH2Af8BVAFtAfUB/wFB + AVsB7wH/KAAB6AGTAegB/wHqAZoB6gH/AesBoAHrAf8B6AGVAegB/wHlAYQB5QH/Ad4BeAHeAf8oAAER + A/8BGAP/AR4D/wETA/8BBQL+Af8BBQLpAf8oAAP3Af8D+QH/A/0B/wP3Af8D7gH/A+EB//8A/wAqAANl + Af8DWgH/A1UB/wNUAf8DUgH/A00B/ygAAZEBcAEeAf8BfgFdARYB/wFzAVQBEwH/AXABUgETAf8BagFN + ARIB/wFZAUMBFAH/KAABOAFiATgB/wErAVMBKwH/ASYBTQEmAf8BJQFLASUB/wEkAUYBJAH/ASMBOwEj + Af8oAAKeAU4B/wKWASkB/wKTARsB/wKSARgB/wKNARUB/wJ7ARgB/yAAA5kB/wNyAf8DcgH/A3AB/wNv + Af8DbgH/A2oB/wNhAf8DVQH/A1YB/xgAAcYBqQFtAf8BtAGCAR4B/wG0AYEBHQH/AbIBgQEdAf8BrwGB + ARwB/wGsAYEBGwH/AaQBewEaAf8BjgFsARgB/wFzAVMBEwH/AVkBTgE1Af8YAAGAAZ4BgAH/AT0BegE9 + Af8BPAF7ATwB/wE7AXoBOwH/AToBeAE6Af8BOAF2ATgB/wE2AXABNgH/ATABYgEwAf8BJgFMASYB/wE+ + AUkBPgH/GAACwAGbAf8CsgFkAf8CtAFiAf8CsgFfAf8CsQFbAf8CsQFXAf8CrQFQAf8CogE6Af8CkwEZ + Af8CagE4Af8UAAOfAf8DgQH/A4QB/wOJAf8DiAH/A4YB/wOGAf8DgwH/A4EB/wNxAf8DWwH/A1UB/xAA + AdEBsQFvAf8B2QGdASQB/wHlAaUBJQH/Ae0BrAEmAf8B6wGqASYB/wHqAagBJQH/AeoBqAElAf8B4wGk + ASMB/wHZAZwBIgH/AbMBgQEdAf8BgAFeARUB/wFYAU0BNQH/EAABgAGpAYAB/wE+AZgBPgH/ATkBqAE5 + Af8BNgGzATYB/wE3AbABNwH/ATcBrgE3Af8BNwGuATcB/wE4AaYBOAH/ATwBmQE8Af8BPAF6ATwB/wEq + AVUBKgH/AT0BSQE9Af8QAALJAaAB/wLLAX8B/wLUAYEB/wLaAYMB/wLYAYIB/wLXAYIB/wLXAYIB/wLU + AYEB/wLMAXsB/wK0AWAB/wKZASgB/wJrATYB/xAAA4EB/wOJAf8DkAH/A5AB/wOQAf8DkAH/A4oB/wOM + Af8DgwH/A2sB/wNuAf8DWQH/EAAB3AGgASUB/wHsAaoBJgH/AfYBswErAf8B9gGzASsB/wH2AbMBKwH/ + AfYBswErAf8B7wGtASYB/wHyAbABJwH/AeIBpAElAf8BpAF8ARwB/wGrAYABHAH/AX4BXQEVAf8QAAE9 + AZ0BPQH/ATYBsQE2Af8BNAHAATQB/wE0AcABNAH/ATQBwAE0Af8BNAHAATQB/wE1AbYBNQH/ATMBuwEz + Af8BOgGkAToB/wE4AXABOAH/ATkBdQE5Af8BKQFUASkB/xAAAs0BgQH/AtgBggH/At8BjQH/At8BjQH/ + At8BjQH/At8BjQH/AtsBgwH/At0BhwH/AtIBgQH/Aq0BVAH/ArABVwH/ApgBJAH/DAADiQH/A44B/wOQ + Af8DkAH/A5AB/wOPAf8DiwH/A3cB/wNXAf8DNAH/AwUB/wNtAf8DagH/A1MB/wgAAesBrAEpAf8B8gGy + ASwB/wH4AbQBKQH/AfYBswErAf8B9gGzASsB/wH1AbIBKwH/AewBrAEqAf8BvgGKASIB/wF1AVYBFgH/ + ASsBIQELAf8DBQH/AaoBgAEcAf8BpAF7ARoB/wFqAVABFQH/CAABOQGxATkB/wE3AbsBNwH/ATABxAEw + Af8BNAHAATQB/wE0AcABNAH/ATQBvwE0Af8BOgGyAToB/wFDAYABQwH/ASoBTgEqAf8BEQEdAREB/wMF + Af8BOQF0ATkB/wE2AXABNgH/AScBRgEnAf8IAALYAYcB/wLcAY8B/wLhAYsB/wLfAY0B/wLfAY0B/wLe + AY0B/wLYAYoB/wK1AXQB/wKPASYB/wJOAQUB/wMFAf8CrgFXAf8CrQFQAf8CiAEeAf8IAAORAf8DnAH/ + A5EB/wOQAf8DfAH/A14B/wM9Af8DCwH/AwUB/wMFAf8DLQH/A4EB/wN4Af8DXgH/CAAB+QG2ASoC/wG/ + ATgB/wH4AbcBLQH/AfYBswErAf8BygGTASQB/wGBAWIBGQH/ATkBKwENAf8CBgEFAf8DBQH/AwUB/wEh + ARkBCQH/AeABowEkAf8BxAGOAR8B/wGEAWQBFwH/CAABMQHGATEB/wE3AdEBNwH/ATUBwwE1Af8BNAHA + ATQB/wFDAYcBQwH/AS8BWAEvAf8BFgEnARYB/wMFAf8DBQH/AwUB/wEOARcBDgH/ATsBoQE7Af8BPwGB + AT8B/wEtAVsBLQH/CAAC4gGNAf8C5QGhAf8C3wGSAf8C3wGNAf8CvwF7Af8CmAE1Af8CXwEFAf8CHgEF + Af8DBQH/AwUB/wJFAQUB/wLRAYEB/wK7AXEB/wKeATIB/wgAA5oB/wOmAf8DlQH/A5AB/wNoAf8DUAH/ + AyEB/wMFAf8DBQH/AwUB/wNZAf8DjQH/A4EB/wNkAf8IAAH/Ab4BNAL/AcUBRwH/AfoBugExAf8B9gGz + ASsB/wGZAXQBHQH/AWUBSgEUAf8BFAEQAQcB/wMFAf8DBQH/AwUB/wF6AVoBFwH/AfQBsQEoAf8B2AGb + ASEB/wGVAXABGAH/CAABNAHQATQB/wFBAdMBQQH/ATcBxgE3Af8BNAHAATQB/wE3AWgBNwH/ASUBQwEl + Af8BCgEPAQoB/wMFAf8DBQH/AwUB/wEsAVEBLAH/ATIBvgEyAf8BPAGXATwB/wEyAWYBMgH/CAAC5QGc + Af8C6AGtAf8C4QGYAf8C3wGNAf8CpAFOAf8ChQEYAf8COQEFAf8DBQH/AwUB/wMFAf8CkgErAf8C3gGI + Af8CywF7Af8CpQFAAf8IAAOfAf8DqgH/A5UB/wOQAf8DjwH/A48B/wNGAf8DBQH/AwUB/wMTAf8DewH/ + A4gB/wOCAf8DZgH/CAAB/wHBAT0C/wHIAU8B/wH5AbkBMgH/AfYBswErAf8B9QGyASsB/wH0AbIBKwH/ + AUsBOAEQAf8DBQH/AwUB/wEKAQkBBQH/AccBkQEkAf8B6wGqASYB/wHiAaMBJAH/AZoBdAEZAf8IAAE6 + AdIBOgH/AUYB1QFGAf8BOAHFATgB/wE0AcABNAH/ATQBvwE0Af8BNQG+ATUB/wEcATIBHAH/AwUB/wMF + Af8BBwEIAQcB/wFDAYQBQwH/ATcBsAE3Af8BOgGjAToB/wEzAWoBMwH/CAAC5gGkAf8C6wG0Af8C4QGY + Af8C3wGNAf8C3gGNAf8C3gGNAf8CcQELAf8DBQH/AwUB/wIuAQUB/wK9AXoB/wLYAYIB/wLRAYEB/wKo + AUUB/wgAA54B/wOxAf8DngH/A40B/wOKAf8DTQH/AwUB/wNZAf8DFQH/A0YB/wOOAf8DkAH/A4EB/wNm + Af8IAAH/AcABOwL/AcwBWgL/AcEBOgH/Ae8BrwErAf8B6wGrASkB/wFaAUIBEgH/AwUB/wF5AVkBFwH/ + AQsBCQEFAf8BSwE4ARAB/wHzAbEBKwH/AfYBswErAf8B3AGfASMB/wGZAXQBGgH/CAABOAHSATgB/wFO + AdYBTgH/ATgB0AE4Af8BOQG2ATkB/wE6AbABOgH/ASEBPAEhAf8DBQH/ASsBUAErAf8BBwEJAQcB/wEc + ATIBHAH/ATYBvQE2Af8BNAHAATQB/wE8AZwBPAH/ATQBaQE0Af8IAALmAaIB/wLtAb0B/wLlAaIB/wLb + AYsB/wLYAYgB/wJ/ARIB/wJOAQUB/wKSASkB/wIyAQUB/wJxAQsB/wLdAY0B/wLfAY0B/wLNAX8B/wKn + AUcB/wgAA5kB/wO2Af8DrQH/A5EB/wNNAf8DBQH/A2YB/wOTAf8DRAH/A2gB/wORAf8DkAH/A4EB/wNq + Af8IAAH/AbwBMgL/Ac4BYQL/AckBUwH/AfEBtAE0Af8BWgFCARIB/wMFAf8BlAFxARwB/wH6AbYBLAH/ + AUcBNQEPAf8BmQF0AR0B/wH5AbUBLAH/AfYBswErAf8B2QGdASMB/wGhAXsBHgH/CAABMwHPATMB/wFT + AdcBUwH/AUkB1QFJAf8BQQG2AUEB/wEhATwBIQH/AwUB/wE2AWUBNgH/ATEBxwExAf8BGwEwARsB/wE3 + AWgBNwH/ATIBxgEyAf8BNAHAATQB/wE9AZkBPQH/AToBbgE6Af8IAALkAZkB/wLuAcIB/wLsAbgB/wLa + AZgB/wJ/ARIB/wMFAf8CoQFKAf8C4gGQAf8CbQEJAf8CpAFOAf8C4QGQAf8C3wGNAf8CzAF/Af8CqAFV + Af8MAAO4Af8DuwH/A3YB/wMLAf8DZwH/A5EB/wOOAf8DdgH/A4MB/wOQAf8DkAH/A4EB/xAAAf8BzgFi + Av8B0AFnAf8BtQGJASkB/wIGAQUB/wGYAXQBHQH/AfgBtAErAf8B8wGxASsB/wG6AYcBIgH/Ad8BogEo + Af8B9gGzASsB/wH2AbMBKwH/Ad8BoQEkAf8QAAFUAdgBVAH/AVcB2AFXAf8BSgF4AUoB/wMFAf8BNwFo + ATcB/wEzAcQBMwH/ATYBvQE2Af8BQgF9AUIB/wE+AaABPgH/ATQBwAE0Af8BNAHAATQB/wE8AZ8BPAH/ + EAAC7wHEAf8C8AHIAf8CrgF7Af8CHgEFAf8CpAFOAf8C4QGPAf8C3QGNAf8CtAFxAf8C0AGCAf8C3wGN + Af8C3wGNAf8CzwGBAf8QAAPKAf8DwAH/A5UB/wOBAf8DngH/A5UB/wOUAf8DkAH/A5AB/wOQAf8DkAH/ + A6EB/xAAAf8B3QGOAv8B0wFwAf8B7QG4AT4B/wHHAZgBLwH/Af4BwAE7Af8B9wG5ATMB/wH3AbcBMQH/ + AfYBswErAf8B9gGzASsB/wH2AbMBKwH/AfYBswErAf8B2QG0AWwB/xAAAYoB5gGKAf8BXQHaAV0B/wFQ + Aa4BUAH/AU0BhAFNAf8BPAHNATwB/wE7AcEBOwH/ATkBwQE5Af8BNAHAATQB/wE0AcABNAH/ATQBwAE0 + Af8BNAHAATQB/wF6AbMBegH/EAAC8wHPAf8C8QHPAf8C1gGlAf8CvAGFAf8C5AGkAf8C3gGZAf8C3wGW + Af8C3wGNAf8C3wGNAf8C3wGNAf8C3wGNAf8C0AGhAf8UAAPPAf8DuwH/A7YB/wOuAf8DowH/A5cB/wOR + Af8DjQH/A4wB/wOlAf8YAAH/Ad8BlQL/AdABaAL/Ac0BYQL/AcsBVwL/AcMBRAH/Af0BuwEyAf8B9wG3 + AS4B/wHyAbEBKQH/AfIBrwEnAf8B4AG6AW4B/xgAAY8B5wGPAf8BWAHZAVgB/wFTAdgBUwH/AUwB1gFM + Af8BPwHTAT8B/wE1AcwBNQH/ATYBwgE2Af8BNQG7ATUB/wEyAbsBMgH/AXoBvAF6Af8YAAL0AdUB/wLw + AcgB/wLvAcMB/wLtAbsB/wLoAaoB/wLjAZkB/wLfAZIB/wLdAYoB/wLdAYcB/wLWAaQB/yAAA6IB/wOm + Af8DqgH/A6MB/wOZAf8DjAH/KAAB/wHDAUIC/wHGAUoC/wHJAVEC/wHCAUQC/wG9ATIB/wHyAa8BJwH/ + KAABPQHTAT0B/wFDAdQBQwH/AUcB1QFHAf8BPwHTAT8B/wEzAc8BMwH/ATMBuwEzAf8oAALnAagB/wLq + AbAB/wLrAbUB/wLoAaoB/wLkAZkB/wLdAYcB//8A/wAqAAItAVUB/wIjAUgB/wIfAUMB/wIeAUEB/wId + AT4B/wIdATMB/ygAAX0BBQFYAf8BZgEFAUkB/wFcAQUBQgH/AVoBBQFAAf8BVQEFAT0B/wFLAQUBNgH/ + KAABJAFGAVMB/wEbATsBSAH/ARcBNgFDAf8BFwE1AUEB/wEWATIBPQH/ARcBKwEyAf8oAAOFAf8DeAH/ + A3AB/wNuAf8DagH/A2EB/yAAAmUBhwH/AjABagH/Ai8BagH/Ai8BaQH/Ai4BaAH/Ai0BZQH/AisBYQH/ + AiYBVAH/Ah4BQwH/AjEBPgH/GAABrAFAAYkB/wGPAQUBaQH/AY8BBQFpAf8BjQEFAWgB/wGLAQUBZgH/ + AYgBBQFkAf8BggEFAWAB/wF2AQUBUwH/AVwBBQFBAf8BTgEhAUAB/xgAAV0BfgGGAf8BJQFVAWkB/wEj + AVYBagH/ASMBVAFpAf8BIgFTAWcB/wEhAVIBZQH/ASABTgFhAf8BHQFEAVQB/wEXATYBQwH/AS0BOQE9 + Af8YAAO0Af8DlwH/A5cB/wOVAf8DkwH/A5IB/wONAf8DgQH/A3AB/wNfAf8UAAJkAZIB/wIvAYQB/wIq + AZEB/wInAZsB/wIoAZkB/wIoAZgB/wIoAZgB/wIqAZAB/wIuAYQB/wIvAWoB/wIiAUoB/wIwAT0B/xAA + AbUBQQGQAf8BrQEFAX4B/wG2AQUBgQH/AbwBBQGDAf8BuwEFAYMB/wG6AQUBggH/AboBBQGCAf8BtAEF + AYEB/wGsAQUBfQH/AY4BBQFoAf8BZwEFAUkB/wFNASEBQAH/EAABWgGBAZIB/wEfAWsBhQH/ARgBdAGU + Af8BEwF6AZ8B/wEUAXkBnQH/ARUBeAGbAf8BFQF4AZsB/wEYAXMBkwH/AR0BawGGAf8BIwFVAWoB/wEa + ATwBSQH/AS0BOAE9Af8QAAO6Af8DrAH/A7IB/wO2Af8DtQH/A7QB/wO0Af8DsQH/A6sB/wOWAf8DeQH/ + A18B/xAAAi8BhwH/AicBmgH/AiQBqAH/AiQBqAH/AiQBqAH/AiQBqAH/AiYBngH/AiQBowH/AiwBjwH/ + AiwBYQH/Ai0BZQH/AiEBSAH/EAABsAEFAYAB/wG8AQUBgwH/AcYBBQGKAf8BxgEFAYoB/wHGAQUBigH/ + AcYBBQGKAf8BvgEFAYUB/wHBAQUBhwH/AbQBBQGBAf8BgwEFAWAB/wGIAQUBZAH/AWUBBQFIAf8QAAEe + AW4BiQH/ARMBeQGeAf8BDgGBAa0B/wEOAYEBrQH/AQ4BgQGtAf8BDgGBAa0B/wERAXwBowH/AQ8BfwGn + Af8BGgFyAZEB/wEiAU8BYQH/ASIBUQFlAf8BGQE7AUgB/xAAA64B/wO2Af8DvQH/A70B/wO9Af8DvQH/ + A7cB/wO5Af8DsQH/A44B/wOSAf8DdwH/DAACKgGZAf8CKAGiAf8CIQGrAf8CJAGoAf8CJAGoAf8CJQGn + Af8CKgGbAf8CNQFvAf8CIgFDAf8CDwEaAf8DBQH/Ai0BZAH/AisBYQH/AiABPQH/CAABvQEFAYQB/wHF + AQUBiQH/AcYBBQGKAf8BxgEFAYoB/wHGAQUBigH/AcYBBQGKAf8BvwEFAYUB/wGZAQUBcAH/AWABBQFE + Af8BJAEFARoB/wMFAf8BhwEFAWMB/wGCAQUBYAH/AVgBBQE/Af8IAAEWAXoBnQH/ARMBgAGnAf8BCwGB + AbEB/wEOAYEBrQH/AQ4BgQGtAf8BDwGBAasB/wEXAXsBngH/ASkBWgFuAf8BGwE3AUMB/wEMARYBGgH/ + AwUB/wEiAVEBZAH/ASABTgFhAf8BGQEyATwB/wgAA7YB/wO7Af8DvQH/A70B/wO9Af8DvAH/A7gB/wOe + Af8DcwH/AzgB/wMFAf8DkQH/A40B/wNsAf8IAAIhAa0B/wIkAbkB/wIlAaoB/wIkAagB/wI1AXkB/wIm + AUwB/wISASIB/wMFAf8DBQH/AwUB/wIMARQB/wIsAYwB/wIyAXQB/wIkAU8B/wgAAcgBBQGMAf8B1wEF + AZYB/wHJAQUBjAH/AcYBBQGKAf8BowEFAXcB/wFtAQUBTgH/AS8BBQEiAf8BBgEFAQYB/wMFAf8DBQH/ + ARsBBQEUAf8BswEFAYEB/wGbAQUBcQH/AW4BBQFOAf8IAAELAYEBswH/AQsBigG/Af8BDwGBAa8B/wEO + AYEBrQH/AScBYQF5Af8BHgE/AUwB/wEPARwBIgH/AwUB/wMFAf8DBQH/AQoBEQEUAf8BGwFxAY4B/wEl + AV0BdAH/ARwBQAFPAf8IAAO+Af8DxwH/A74B/wO9Af8DpAH/A34B/wNGAf8DCgH/AwUB/wMFAf8DLgH/ + A68B/wOfAf8DfwH/CAACIwG3Af8CJQHCAf8CJwGtAf8CJAGoAf8CLAFaAf8CHgE6Af8CCAENAf8DBQH/ + AwUB/wMFAf8CIwFGAf8CIwGmAf8CLgGDAf8CJwFZAf8IAAHUAQUBlAH/AeEBBQGdAf8BzgEFAY8B/wHG + AQUBigH/AYEBBQFbAf8BUwEFATsB/wERAQUBDQH/AwUB/wMFAf8DBQH/AWQBBQFHAf8BwwEFAYgB/wGr + AQUBfAH/AXsBBQFXAf8IAAELAYgBvQH/AQsBkAHIAf8BEAGCAbIB/wEOAYEBrQH/ASIBSQFZAf8BGAEw + AToB/wEHAQsBDQH/AwUB/wMFAf8DBQH/ARwBOQFFAf8BDQGBAasB/wEeAWoBhAH/AR0BSAFZAf8IAAPF + Af8DzgH/A8EB/wO9Af8DiQH/A2cB/wMgAf8DBQH/AwUB/wMFAf8DdwH/A7oB/wOqAf8DhAH/CAACJAG8 + Af8CJgHHAf8CKAGsAf8CJAGoAf8CJQGnAf8CJgGlAf8CFwEsAf8DBQH/AwUB/wIGAQgB/wI1AXcB/wIo + AZkB/wIrAY0B/wIoAVwB/wgAAdoBBQGYAf8B5wEFAaEB/wHOAQUBjwH/AcYBBQGKAf8BxgEFAYoB/wHF + AQUBigH/AT0BBQEsAf8DBQH/AwUB/wEJAQUBCAH/AaEBBQF1Af8BuwEFAYMB/wGzAQUBgQH/AX8BBQFa + Af8IAAELAYwBwgH/AQwBlAHNAf8BEQGBAbEB/wEOAYEBrQH/AQ8BgQGrAf8BEAGBAaoB/wESASQBKwH/ + AwUB/wMFAf8BBgEHAQgB/wEnAV8BdgH/ARQBeQGdAf8BGQFyAZAB/wEeAUoBXAH/CAADyQH/A9EB/wPB + Af8DvQH/A7wB/wO8Af8DVQH/AwUB/wMFAf8DEQH/A6MB/wO1Af8DsAH/A4cB/wgAAiQBuwH/AicBzgH/ + AiUBuQH/AikBngH/AisBmQH/AhsBNAH/AwUB/wIjAUUB/wIGAQgB/wIXASwB/wImAaQB/wIkAagB/wIu + AYcB/wIpAVoB/wgAAdkBBQGXAf8B7gEFAaYB/wHXAQUBlgH/AcIBBQGHAf8BvgEFAYUB/wFJAQUBNAH/ + AwUB/wFjAQUBRgH/AQoBBQEIAf8BPQEFASwB/wHFAQUBiQH/AcYBBQGKAf8BrwEFAX8B/wF/AQUBWgH/ + CAABCwGLAcEB/wEMAZkB1AH/AQwBiQG+Af8BFQF9AaIB/wEXAXoBnQH/ARUBKwE0Af8DBQH/ARsBOQFF + Af8BBgIIAf8BEgEkASsB/wERAYABqQH/AQ4BgQGtAf8BHQFtAYkB/wEfAUkBWgH/CAADyAH/A9YB/wPI + Af8DugH/A7cB/wNgAf8DBQH/A3YB/wMTAf8DVQH/A7sB/wO9Af8DrQH/A4cB/wgAAiMBtQH/AigB0QH/ + AiYByQH/AjEBngH/AhsBNAH/AwUB/wIrAVcB/wIiAa4B/wIWASkB/wIsAVoB/wIiAa0B/wIkAagB/wIv + AYQB/wIuAV8B/wgAAdIBBQGSAf8B8wEFAaoB/wHqAQUBowH/AckBBQGMAf8BSQEFATQB/wMFAf8BfQEF + AVkB/wHKAQUBjQH/AToBBQEqAf8BgQEFAVsB/wHJAQUBjAH/AcYBBQGKAf8BrQEFAX4B/wGCAQUBYAH/ + CAABCwGHAbsB/wEMAZwB2QH/AQwBlgHQAf8BHQF+AaEB/wEVASsBNAH/AwUB/wEiAUcBVwH/AQsBggG0 + Af8BEgEiASkB/wEiAUkBWQH/AQwBgQGzAf8BDgGBAa0B/wEeAWwBhgH/ASQBTQFeAf8IAAPEAf8D2QH/ + A9MB/wO+Af8DYAH/AwUB/wOGAf8DvwH/A1EB/wOJAf8DvgH/A70B/wOsAf8DjQH/DAACKAHTAf8CKAHW + Af8CPAFnAf8DBQH/AiwBWQH/AiMBqwH/AiYBpAH/AjUBbAH/Ai8BiwH/AiQBqAH/AiQBqAH/Ai0BigH/ + EAAB9AEFAaoB/wH4AQUBrQH/AZgBBQFvAf8BBgEFAQYB/wGAAQUBWwH/AcgBBQGMAf8BxQEFAYkB/wGW + AQUBbgH/AbQBBQGBAf8BxgEFAYoB/wHGAQUBigH/AbEBBQGBAf8QAAEMAZ0B2QH/AQwBnwHdAf8BMQFW + AWQB/wMFAf8BIgFJAVkB/wENAYEBsQH/AREBgAGpAf8BKQFYAWsB/wEeAXABjAH/AQ4BgQGtAf8BDgGB + Aa0B/wEcAW8BjAH/EAAD2gH/A9wB/wOdAf8DCgH/A4gB/wO+Af8DuwH/A5wB/wOxAf8DvQH/A70B/wOu + Af8QAAJkAdQB/wIqAdoB/wI+AZYB/wI+AXYB/wIoAbYB/wIrAagB/wIpAagB/wIkAagB/wIkAagB/wIk + AagB/wIkAagB/wJdAZ4B/xAAAesBTQG5Af8B/gEFAbEB/wHOAQUBkAH/AakBBQF7Af8B1wEFAZYB/wHN + AQUBjwH/AcsBBQGOAf8BxgEFAYoB/wHGAQUBigH/AcYBBQGKAf8BxgEFAYoB/wG8AT8BlQH/EAABUQGw + AdkB/wEMAaMB4gH/ASwBewGWAf8BMQFhAXUB/wEQAYgBuwH/ARUBgQGsAf8BEwGBAa0B/wEOAYEBrQH/ + AQ4BgQGtAf8BDgGBAa0B/wEOAYEBrQH/AVEBhwGfAf8QAAPiAf8D3wH/A8EB/wOpAf8DyAH/A8EB/wPA + Af8DvQH/A70B/wO9Af8DvQH/A70B/xQAAmUB2QH/AigB1gH/AigB0gH/AicBzAH/AiUBvwH/AiUBsgH/ + AiYBqQH/AiYBowH/AiMBowH/AlwBpwH/GAAB8QFNAb4B/wH5AQUBrQH/AfQBBQGqAf8B7QEFAaUB/wHf + AQUBmwH/AdEBBQGSAf8ByQEFAYwB/wHDAQUBiAH/AcEBBQGHAf8BwgFBAZkB/xgAAVIBtAHeAf8BDAGf + Ad0B/wEMAZwB2QH/AQwBmAHTAf8BCwGPAcYB/wENAYUBuAH/ARABgQGvAf8BEQF/AacB/wEOAX8BqAH/ + AU4BjQGpAf8YAAPmAf8D3AH/A9kB/wPUAf8DzAH/A8MB/wO+Af8DugH/A7kB/wPBAf8gAAIkAb8B/wIl + AcMB/wImAcgB/wIlAb8B/wIjAbUB/wIkAaMB/ygAAd0BBQGaAf8B4wEFAZ4B/wHoAQUBogH/Ad8BBQGb + Af8B0gEFAZIB/wHBAQUBhwH/KAABCwGOAcUB/wELAZEBygH/AQwBlQHOAf8BCwGPAcYB/wELAYcBuwH/ + AQ8BfwGnAf8oAAPLAf8DzgH/A9EB/wPMAf8DxAH/A7kB//8A/wAqAANCAf8DOwH/AzkB/wM4Af8DNwH/ + AzUB/ygAAXEBSwE+Af8BYAE7AS8B/wFYATUBKgH/AVYBNAEpAf8BUQEyAScB/wFDAS4BJgH/KAABKAFE + ASgB/wEfAToBHwH/ARsBNgEbAf8BGwE0ARsB/wEaATEBGgH/ARkBKgEZAf8oAAFSAVQBLQH/AUYBSAEi + Af8BQAFDAR4B/wE/AUEBHQH/ATwBPgEcAf8BMgEzARwB/yAAA4IB/wNMAf8DTAH/A0sB/wNKAf8DSQH/ + A0YB/wNAAf8DOQH/A0gB/xgAAaIBgQF0Af8BhwFVAUMB/wGIAVUBQgH/AYYBVAFBAf8BhAFSAUAB/wGC + AVABPgH/AYEBTQE7Af8BcAFDATUB/wFYATUBKQH/AUgBPAE3Af8YAAFgAXwBYAH/ASsBVQErAf8BKgFV + ASoB/wEqAVQBKgH/ASkBUwEpAf8BKAFSASgB/wEmAU4BJgH/ASIBRAEiAf8BGwE2ARsB/wEvATgBLwH/ + GAABhQGHAWQB/wFmAWoBLwH/AWcBagEuAf8BZgFpAS4B/wFkAWgBLQH/AWIBZgEsAf8BXgFiASoB/wFS + AVUBJQH/AUABQwEeAf8BPQE+ATEB/xQAA4gB/wNeAf8DZAH/A2kB/wNoAf8DZgH/A2YB/wNjAf8DXQH/ + A0wB/wM8Af8DSAH/EAABsAGBAXMB/wGwAWABQwH/AcIBYAE9Af8BzQFhATsB/wHKAWEBPAH/AckBYAE7 + Af8ByQFgATsB/wHBAV8BPAH/AbEBXgFBAf8BhwFUAUIB/wFiATsBLgH/AUcBOgE2Af8QAAFgAYEBYAH/ + ASwBbQEsAf8BKAF4ASgB/wEmAX8BJgH/AScBfQEnAf8BJwF8AScB/wEnAXwBJwH/ASgBdgEoAf8BKgFt + ASoB/wEqAVUBKgH/AR4BOwEeAf8BLgE4AS4B/xAAAY8BkgFjAf8BgQGEAS4B/wGLAZIBKQH/AZQBnAEl + Af8BkgGZASYB/wGRAZgBJwH/AZEBmAEnAf8BigGQASgB/wGBAYQBLAH/AWYBagEuAf8BSAFKASEB/wI9 + ATAB/xAAA2AB/wNpAf8DcgH/A3IB/wNyAf8DcgH/A2oB/wNsAf8DYwH/A0cB/wNJAf8DOwH/EAABtQFg + AUIB/wHLAWEBPAH/AdQBaAFCAf8B1AFoAUIB/wHUAWgBQgH/AdQBaAFCAf8BzwFiATwB/wHSAWMBPQH/ + Ab4BYAE/Af8BgQFOAT0B/wGBAVABPgH/AWABOgEtAf8QAAErAXABKwH/ASYBfgEmAf8BJQGDASUB/wEl + AYMBJQH/ASUBgwElAf8BJQGDASUB/wElAYEBJQH/ASQBgQEkAf8BKQF1ASkB/wEoAU4BKAH/ASgBUQEo + Af8BHgE7AR4B/xAAAYIBiAEtAf8BkwGbASYB/wGgAagBIwH/AaABqAEjAf8BoAGoASMB/wGgAagBIwH/ + AZcBnwEkAf8BmwGjASMB/wGIAY4BKgH/AV4BYQErAf8BYgFlASwB/wFGAUkBIAH/DAADaQH/A28B/wNy + Af8DcgH/A3IB/wNwAf8DawH/A1EB/wM6Af8DKQH/AwUB/wNJAf8DRgH/AzgB/wgAAckBZAFAAf8BzwFo + AUQB/wHXAWYBPwH/AdQBaAFCAf8B1AFoAUIB/wHTAWgBQgH/AcoBZgFCAf8BjgFcAUoB/wFZATkBLgH/ + ASEBFgETAf8DBQH/AYEBUAE/Af8BgQFNATsB/wFQATQBKwH/CAABKAF+ASgB/wEnAYEBJwH/ASIBhQEi + Af8BJQGDASUB/wElAYMBJQH/ASUBggElAf8BKQF/ASkB/wEvAVkBLwH/AR4BNgEeAf8BDQEVAQ0B/wMF + Af8BKAFRASgB/wEmAU4BJgH/ARwBMQEcAf8IAAGTAZoBKAH/AZsBowEmAf8BowGrAR8B/wGgAagBIwH/ + AaABqAEjAf8BnwGnASMB/wGUAZsBKQH/AWsBbwE0Af8BQQFDASEB/wEZARoBDgH/AwUB/wFhAWQBLQH/ + AV4BYgEqAf8BOwE9AR8B/wgAA3MB/wOAAf8DcwH/A3IB/wNWAf8DPQH/Ay4B/wMKAf8DBQH/AwUB/wMk + Af8DYQH/A1IB/wM+Af8IAAHYAWgBQQH/AdsBdQFRAf8B1AFrAUUB/wHUAWgBQgH/AZwBXwFJAf8BZQFA + ATQB/wEsAR0BGAH/AgYBBQH/AwUB/wMFAf8BGgERAQ8B/wG7AWABQAH/AZUBWwFFAf8BaQFAATIB/wgA + ASMBhwEjAf8BJQGQASUB/wElAYUBJQH/ASUBgwElAf8BLwFhAS8B/wEiAT4BIgH/ARABHAEQAf8DBQH/ + AwUB/wMFAf8BCwERAQsB/wEqAXMBKgH/AS0BXQEtAf8BIAFAASAB/wgAAaQBrQEgAf8BsAG6ASIB/wGi + AaoBIwH/AaABqAEjAf8BdQF5ATMB/wFKAUwBJQH/ASEBIgESAf8DBQH/AwUB/wMFAf8CFAEMAf8BhgGM + ASsB/wFwAXQBMQH/AUwBTwEkAf8IAAN9Af8DhwH/A3cB/wNyAf8DRAH/AzYB/wMcAf8DBQH/AwUB/wMF + Af8DOwH/A24B/wNcAf8DQgH/CAAB2gFyAU0B/wHdAX4BXQH/AdUBbwFLAf8B1AFoAUIB/wF4AUwBPQH/ + AUwBMQEoAf8BEAEMAQoB/wMFAf8DBQH/AwUB/wFcATsBMAH/AdQBZAE9Af8BrwFeAUEB/wF2AUYBNgH/ + CAABJAGOASQB/wEmAZcBJgH/AScBhwEnAf8BJQGDASUB/wEnAUgBJwH/ARoBLwEaAf8BCAELAQgB/wMF + Af8DBQH/AwUB/wEfATkBHwH/ASQBgQEkAf8BKgFsASoB/wEjAUcBIwH/CAABrgG3ASEB/wG5AcIBIwH/ + AaUBrQElAf8BoAGoASMB/wFXAVoBKwH/ATgBOgEdAf8CDQEIAf8DBQH/AwUB/wMFAf8BRAFGASMB/wGe + AaYBIQH/AYEBgwEsAf8BVgFZAScB/wgAA4EB/wOMAf8DdwH/A3IB/wNwAf8DcAH/AzIB/wMFAf8DBQH/ + AxEB/wNVAf8DaAH/A2IB/wNDAf8IAAHcAXgBVAH/Ad8BgQFjAf8B1AFvAUwB/wHUAWgBQgH/AdMBaAFC + Af8B0gFoAUMB/wE5ASUBHgH/AwUB/wMFAf8BCQIHAf8BmQFfAUkB/wHKAWEBPAH/Ab0BXwE+Af8BegFI + ATgB/wgAASUBkgElAf8BJwGbAScB/wEnAYYBJwH/ASUBgwElAf8BJQGCASUB/wEmAYEBJgH/ARQBIwEU + Af8DBQH/AwUB/wEGAQcBBgH/AS8BXwEvAf8BJwF9AScB/wEpAXQBKQH/ASQBSgEkAf8IAAGzAbwBIgH/ + Ab0BxwEkAf8BpAGsASYB/wGgAagBIwH/AZ8BpwEjAf8BngGmASQB/wEqASsBFgH/AwUB/wMFAf8CCAEG + Af8BcwF3ATQB/wGSAZkBJgH/AYgBjgEqAf8BWQFcASgB/wgAA4EB/wOWAf8DgQH/A24B/wNqAf8DNQH/ + AwUB/wM7Af8DEwH/AzIB/wNvAf8DcgH/A18B/wNDAf8IAAHcAXcBUwH/AeEBhgFsAf8B2gF2AVIB/wHM + AWcBRAH/AckBZQFCAf8BRAEsASQB/wMFAf8BXAE6AS8B/wEJAQgBBwH/ATkBJQEeAf8B0QFoAUMB/wHU + AWgBQgH/AbUBXwFBAf8BeAFJATkB/wgAASUBkgElAf8BKAGgASgB/wEmAZABJgH/ASgBgQEoAf8BKQF9 + ASkB/wEYASoBGAH/AwUB/wEfATgBHwH/AQYBBwEGAf8BFAEjARQB/wEmAYEBJgH/ASUBgwElAf8BKgFw + ASoB/wElAUkBJQH/CAABsgG7ASIB/wHEAc4BJQH/AbABuQEjAf8BlwGeASgB/wGTAZoBKQH/ATIBNAEa + Af8DBQH/AUMBRQEiAf8CCAEGAf8BKgErARYB/wGdAaQBJQH/AaABqAEjAf8BggGHASwB/wFYAVsBKAH/ + CAADfAH/A5sB/wOQAf8DcwH/AzUB/wMFAf8DQwH/A3QB/wMxAf8DRAH/A3MB/wNyAf8DXgH/A0YB/wgA + AdoBcAFLAf8B4gGKAXIB/wHfAYEBZwH/AcgBcAFRAf8BRAEsASQB/wMFAf8BdAFKATsB/wHYAWoBQwH/ + ATYBIwEdAf8BeAFMAT0B/wHXAWkBQwH/AdQBaAFCAf8BsQFfAUIB/wF+AU8BQAH/CAABJAGNASQB/wEp + AaQBKQH/ASgBnQEoAf8BLgGBAS4B/wEYASoBGAH/AwUB/wEmAUYBJgH/ASMBiAEjAf8BFAEiARQB/wEn + AUgBJwH/ASMBhwEjAf8BJQGDASUB/wErAW0BKwH/ASkBTAEpAf8IAAGsAbUBIQH/AcgB0gElAf8BwAHK + ASQB/wGYAZ4BLwH/ATIBNAEaAf8DBQH/AVUBVwEqAf8BpgGvASAB/wEoASkBFQH/AVcBWgErAf8BpQGt + ASEB/wGgAagBIwH/AYEBhAEtAf8BXAFeAS4B/wwAA54B/wOiAf8DUQH/AwoB/wNEAf8DcwH/A28B/wNQ + Af8DYwH/A3IB/wNyAf8DYAH/EAAB4gGLAXMB/wHjAY8BdwH/AYQBYAFSAf8CBgEFAf8BdwFLAT0B/wHW + AWkBQwH/AdEBaAFDAf8BiwFbAUkB/wG6AWIBRAH/AdQBaAFCAf8B1AFoAUIB/wG5AWABQQH/EAABKQGk + ASkB/wEqAacBKgH/ATQBUwE0Af8DBQH/AScBSAEnAf8BJAGFASQB/wEmAYEBJgH/AS8BVwEvAf8BLAFy + ASwB/wElAYMBJQH/ASUBgwElAf8BKgFyASoB/xAAAcgB0wEmAf8BzAHWASYB/wFkAWYBOwH/AwUB/wFX + AVkBKwH/AaMBqwEhAf8BnQGkASUB/wFpAWwBNAH/AYUBiwEuAf8BoAGoASMB/wGgAagBIwH/AYQBigEs + Af8QAAO3Af8DqAH/A3cB/wNbAf8DgQH/A3cB/wN2Af8DcgH/A3IB/wNyAf8DcgH/A4sB/xAAAegBoAGH + Af8B5AGUAX4B/wG9AXsBYwH/AZgBZwFVAf8B1wF3AVUB/wHQAXEBTwH/AdEBbgFLAf8B1AFoAUIB/wHU + AWgBQgH/AdQBaAFCAf8B1AFoAUIB/wHAAYEBbAH/EAABZQG1AWUB/wErAasBKwH/ATgBfAE4Af8BNgFf + ATYB/wEoAY4BKAH/ASoBgwEqAf8BKAGEASgB/wElAYMBJQH/ASUBgwElAf8BJQGDASUB/wElAYMBJQH/ + AVsBiAFbAf8QAAHNAdQBYgH/AdEB3AEnAf8BkAGWAT0B/wFyAXYBPQH/Aa0BtgEmAf8BoQGoASkB/wGh + AakBJwH/AaABqAEjAf8BoAGoASMB/wGgAagBIwH/AaABqAEjAf8BmgGeAVwB/xQAA74B/wOiAf8DmwH/ + A5IB/wODAf8DegH/A3MB/wNuAf8DbAH/A48B/xgAAekBpQGPAf8B4wGPAXgB/wHiAYsBcgH/AeABhAFq + Af8B3QF8AVoB/wHYAXABTAH/AdQBawFGAf8B0AFmAUEB/wHTAWMBPAH/AcoBgQFqAf8YAAFmAboBZgH/ + ASoBpwEqAf8BKQGkASkB/wEoAZ8BKAH/ASYBlgEmAf8BJQGLASUB/wEmAYQBJgH/ASYBgQEmAf8BJAGB + ASQB/wFbAZABWwH/GAAB0wHaAWMB/wHMAdcBJgH/AcgB0gEmAf8BwgHMASUB/wG3AcABIwH/AaoBswEj + Af8BoQGqASQB/wGbAaMBJAH/AZwBpAEiAf8BogGnAVsB/yAAA4IB/wOHAf8DjAH/A4MB/wN8Af8DbAH/ + KAAB3QF7AVkB/wHeAYABXwH/Ad8BgQFlAf8B3QF8AVoB/wHaAXABSwH/AdIBYwE9Af8oAAEmAZUBJgH/ + AScBmQEnAf8BJwGcAScB/wEmAZYBJgH/ASQBjQEkAf8BJAGBASQB/ygAAbYBvwEiAf8BugHEASMB/wG+ + AcgBJAH/AbcBwAEjAf8BrAG1ASEB/wGbAaMBIwH//wAVAAFCAU0BPgcAAT4DAAEoAwABQAMAAWADAAEB + AQABAQYAAQMWAAP/gQAI/wH4AR8B+AEfAfgBHwH4AR8B4AEHAeABBwHgAQcB4AEHAcABAwHAAQMBwAED + AcABAwHAAQMBwAEDAcABAwHAAQMBgAEBAYABAQGAAQEBgAEBAYABAQGAAQEBgAEBAYABAQGAAQEBgAEB + AYABAQGAAQEBgAEBAYABAQGAAQEBgAEBAYABAQGAAQEBgAEBAYABAQGAAQEBgAEBAYABAQGAAQEBwAED + AcABAwHAAQMBwAEDAcABAwHAAQMBwAEDAcABAwHgAQcB4AEHAeABBwHgAQcB+AEfAfgBHwH4AR8B+AEf + EP8B+AEfAfgBHwH4AR8B+AEfAeABBwHgAQcB4AEHAeABBwHAAQMBwAEDAcABAwHAAQMBwAEDAcABAwHA + AQMBwAEDAYABAQGAAQEBgAEBAYABAQGAAQEBgAEBAYABAQGAAQEBgAEBAYABAQGAAQEBgAEBAYABAQGA + AQEBgAEBAYABAQGAAQEBgAEBAYABAQGgAQEBgAEBAYABAQGAAQEBsAEBAcABAwHAAQMBwAEDAfABAwHA + AQMBwAEDAcABAwHQAQMB4AEHAeABBwHgAQcB/gEHAfgBHwH4AR8B+AEfAfgBHxD/AfgBHwH4AR8B+AEf + AfgBHwHgAQcB4AEHAeABBwHgAQcBwAEDAcABAwHAAQMBwAEDAcABAwHAAQMBwAEDAcABAwGAAQEBgAEB + AYABAQGAAQEBgAEBAYABAQGAAQEBgAEBAYABAQGAAQEBgAEBAYABAQGAAQEBgAEBAYABAQGAAQEBgAEB + AYABAQGAAQEBgAEBAYABAQGAAQEBgAEBAYABAQHAAQMBwAEDAcABAwHAAQMBwAEDAcABAwHAAQMBwAED + AeABBwHgAQcB4AEHAeABBwH4AR8B+AEfAfgBHwH4AR8Q/wH4AR8B+AEfAfgBHwH4AR8B4AEHAeABBwHg + AQcB4AEHAcABAwHAAQMBwAEDAcABAwHAAQMBwAEDAcABAwHAAQMBgAEBAYABAQGAAQEBgAEBAYABAQGA + AQEBgAEBAYABAQGAAQEBgAEBAYABAQGAAQEBgAEBAYABAQGAAQEBgAEBAYABAQGAAQEBgAEBAYABAQGA + AQEBgAEBAYABAQGAAQEBwAEDAcABAwHAAQMBwAEDAcABAwHAAQMBwAEDAcABAwHgAQcB4AEHAeABBwHg + AQcB+AEfAfgBHwH4AR8B+AEfEP8B+AEfAfgBHwH4AR8B+AEfAeABBwHgAQcB4AEHAeABBwHAAQMBwAED + AcABAwHAAQMBwAEDAcABAwHAAQMBwAEDAYABAQGAAQEBgAEBAYABAQGAAQEBgAEBAYABAQGAAQEBgAEB + AYABAQGAAQEBgAEBAYABAQGAAQEBgAEBAYABAQGAAQEBgAEBAYABAQGAAQEBgAEBAYABAQGAAQEBgAEB + AcABAwHAAQMBwAEDAcABAwHAAQMBwAEDAcABAwHAAQMB4AEHAeABBwHgAQcB4AEHAfgBHwH4AR8B+AEf + AfgBHwj/Cw== + + + + True + + + True + + + True + + \ No newline at end of file diff --git a/Source/Core/Controls/ThingInfoPanel.Designer.cs b/Source/Core/Controls/ThingInfoPanel.Designer.cs new file mode 100644 index 0000000..41112dd --- /dev/null +++ b/Source/Core/Controls/ThingInfoPanel.Designer.cs @@ -0,0 +1,354 @@ +namespace CodeImp.DoomBuilder.Controls +{ + partial class ThingInfoPanel + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.Windows.Forms.Label label5; + System.Windows.Forms.Label label4; + System.Windows.Forms.Label label3; + System.Windows.Forms.Label label2; + System.Windows.Forms.Label label1; + this.infopanel = new System.Windows.Forms.GroupBox(); + this.arg5 = new System.Windows.Forms.Label(); + this.arglbl5 = new System.Windows.Forms.Label(); + this.arglbl4 = new System.Windows.Forms.Label(); + this.arg4 = new System.Windows.Forms.Label(); + this.arglbl3 = new System.Windows.Forms.Label(); + this.arglbl2 = new System.Windows.Forms.Label(); + this.arg3 = new System.Windows.Forms.Label(); + this.arglbl1 = new System.Windows.Forms.Label(); + this.arg2 = new System.Windows.Forms.Label(); + this.arg1 = new System.Windows.Forms.Label(); + this.angle = new System.Windows.Forms.Label(); + this.tag = new System.Windows.Forms.Label(); + this.position = new System.Windows.Forms.Label(); + this.action = new System.Windows.Forms.Label(); + this.type = new System.Windows.Forms.Label(); + this.spritepanel = new System.Windows.Forms.GroupBox(); + this.spritename = new System.Windows.Forms.Label(); + this.spritetex = new System.Windows.Forms.Panel(); + label5 = new System.Windows.Forms.Label(); + label4 = new System.Windows.Forms.Label(); + label3 = new System.Windows.Forms.Label(); + label2 = new System.Windows.Forms.Label(); + label1 = new System.Windows.Forms.Label(); + this.infopanel.SuspendLayout(); + this.spritepanel.SuspendLayout(); + this.SuspendLayout(); + // + // label5 + // + label5.AutoSize = true; + label5.Location = new System.Drawing.Point(165, 77); + label5.Name = "label5"; + label5.Size = new System.Drawing.Size(38, 14); + label5.TabIndex = 8; + label5.Text = "Angle:"; + // + // label4 + // + label4.AutoSize = true; + label4.Location = new System.Drawing.Point(30, 77); + label4.Name = "label4"; + label4.Size = new System.Drawing.Size(28, 14); + label4.TabIndex = 4; + label4.Text = "Tag:"; + // + // label3 + // + label3.AutoSize = true; + label3.Location = new System.Drawing.Point(11, 58); + label3.Name = "label3"; + label3.Size = new System.Drawing.Size(47, 14); + label3.TabIndex = 3; + label3.Text = "Position:"; + // + // label2 + // + label2.AutoSize = true; + label2.Location = new System.Drawing.Point(17, 39); + label2.Name = "label2"; + label2.Size = new System.Drawing.Size(41, 14); + label2.TabIndex = 2; + label2.Text = "Action:"; + // + // label1 + // + label1.AutoSize = true; + label1.Location = new System.Drawing.Point(24, 19); + label1.Name = "label1"; + label1.Size = new System.Drawing.Size(34, 14); + label1.TabIndex = 0; + label1.Text = "Type:"; + // + // infopanel + // + this.infopanel.Controls.Add(this.arg5); + this.infopanel.Controls.Add(this.arglbl5); + this.infopanel.Controls.Add(this.arglbl4); + this.infopanel.Controls.Add(this.arg4); + this.infopanel.Controls.Add(this.arglbl3); + this.infopanel.Controls.Add(this.arglbl2); + this.infopanel.Controls.Add(this.arg3); + this.infopanel.Controls.Add(this.arglbl1); + this.infopanel.Controls.Add(this.arg2); + this.infopanel.Controls.Add(this.arg1); + this.infopanel.Controls.Add(this.angle); + this.infopanel.Controls.Add(label5); + this.infopanel.Controls.Add(this.tag); + this.infopanel.Controls.Add(this.position); + this.infopanel.Controls.Add(this.action); + this.infopanel.Controls.Add(label4); + this.infopanel.Controls.Add(label3); + this.infopanel.Controls.Add(label2); + this.infopanel.Controls.Add(this.type); + this.infopanel.Controls.Add(label1); + this.infopanel.Location = new System.Drawing.Point(0, 0); + this.infopanel.Name = "infopanel"; + this.infopanel.Size = new System.Drawing.Size(473, 100); + this.infopanel.TabIndex = 4; + this.infopanel.TabStop = false; + this.infopanel.Text = " Thing "; + // + // arg5 + // + this.arg5.AutoEllipsis = true; + this.arg5.Location = new System.Drawing.Point(384, 79); + this.arg5.Name = "arg5"; + this.arg5.Size = new System.Drawing.Size(83, 14); + this.arg5.TabIndex = 37; + this.arg5.Text = "Arg 1:"; + // + // arglbl5 + // + this.arglbl5.AutoEllipsis = true; + this.arglbl5.BackColor = System.Drawing.Color.Transparent; + this.arglbl5.Location = new System.Drawing.Point(257, 79); + this.arglbl5.Name = "arglbl5"; + this.arglbl5.Size = new System.Drawing.Size(121, 14); + this.arglbl5.TabIndex = 32; + this.arglbl5.Text = "Arg 1:"; + this.arglbl5.TextAlign = System.Drawing.ContentAlignment.TopRight; + // + // arglbl4 + // + this.arglbl4.AutoEllipsis = true; + this.arglbl4.BackColor = System.Drawing.Color.Transparent; + this.arglbl4.Location = new System.Drawing.Point(257, 64); + this.arglbl4.Name = "arglbl4"; + this.arglbl4.Size = new System.Drawing.Size(121, 14); + this.arglbl4.TabIndex = 31; + this.arglbl4.Text = "Arg 1:"; + this.arglbl4.TextAlign = System.Drawing.ContentAlignment.TopRight; + // + // arg4 + // + this.arg4.AutoEllipsis = true; + this.arg4.Location = new System.Drawing.Point(384, 64); + this.arg4.Name = "arg4"; + this.arg4.Size = new System.Drawing.Size(83, 14); + this.arg4.TabIndex = 36; + this.arg4.Text = "Arg 1:"; + // + // arglbl3 + // + this.arglbl3.AutoEllipsis = true; + this.arglbl3.BackColor = System.Drawing.Color.Transparent; + this.arglbl3.Location = new System.Drawing.Point(257, 49); + this.arglbl3.Name = "arglbl3"; + this.arglbl3.Size = new System.Drawing.Size(121, 14); + this.arglbl3.TabIndex = 30; + this.arglbl3.Text = "Arg 1:"; + this.arglbl3.TextAlign = System.Drawing.ContentAlignment.TopRight; + // + // arglbl2 + // + this.arglbl2.AutoEllipsis = true; + this.arglbl2.BackColor = System.Drawing.Color.Transparent; + this.arglbl2.Location = new System.Drawing.Point(257, 34); + this.arglbl2.Name = "arglbl2"; + this.arglbl2.Size = new System.Drawing.Size(121, 14); + this.arglbl2.TabIndex = 29; + this.arglbl2.Text = "Arg 1:"; + this.arglbl2.TextAlign = System.Drawing.ContentAlignment.TopRight; + // + // arg3 + // + this.arg3.AutoEllipsis = true; + this.arg3.Location = new System.Drawing.Point(384, 49); + this.arg3.Name = "arg3"; + this.arg3.Size = new System.Drawing.Size(83, 14); + this.arg3.TabIndex = 35; + this.arg3.Text = "Arg 1:"; + // + // arglbl1 + // + this.arglbl1.AutoEllipsis = true; + this.arglbl1.BackColor = System.Drawing.Color.Transparent; + this.arglbl1.Location = new System.Drawing.Point(257, 19); + this.arglbl1.Name = "arglbl1"; + this.arglbl1.Size = new System.Drawing.Size(121, 14); + this.arglbl1.TabIndex = 28; + this.arglbl1.Text = "Arg 1:"; + this.arglbl1.TextAlign = System.Drawing.ContentAlignment.TopRight; + // + // arg2 + // + this.arg2.AutoEllipsis = true; + this.arg2.Location = new System.Drawing.Point(384, 34); + this.arg2.Name = "arg2"; + this.arg2.Size = new System.Drawing.Size(83, 14); + this.arg2.TabIndex = 34; + this.arg2.Text = "Arg 1:"; + // + // arg1 + // + this.arg1.AutoEllipsis = true; + this.arg1.Location = new System.Drawing.Point(384, 19); + this.arg1.Name = "arg1"; + this.arg1.Size = new System.Drawing.Size(83, 14); + this.arg1.TabIndex = 33; + this.arg1.Text = "Arg 1:"; + // + // angle + // + this.angle.AutoSize = true; + this.angle.Location = new System.Drawing.Point(206, 77); + this.angle.Name = "angle"; + this.angle.Size = new System.Drawing.Size(13, 14); + this.angle.TabIndex = 11; + this.angle.Text = "0"; + // + // tag + // + this.tag.AutoSize = true; + this.tag.Location = new System.Drawing.Point(61, 77); + this.tag.Name = "tag"; + this.tag.Size = new System.Drawing.Size(13, 14); + this.tag.TabIndex = 7; + this.tag.Text = "0"; + // + // position + // + this.position.AutoSize = true; + this.position.Location = new System.Drawing.Point(61, 58); + this.position.Name = "position"; + this.position.Size = new System.Drawing.Size(91, 14); + this.position.TabIndex = 6; + this.position.Text = "1024, 1024, 1024"; + // + // action + // + this.action.AutoEllipsis = true; + this.action.Location = new System.Drawing.Point(61, 39); + this.action.Name = "action"; + this.action.Size = new System.Drawing.Size(210, 14); + this.action.TabIndex = 5; + this.action.Text = "0 - Spawn a Blue Poopie and Ammo"; + // + // type + // + this.type.AutoSize = true; + this.type.Location = new System.Drawing.Point(61, 19); + this.type.Name = "type"; + this.type.Size = new System.Drawing.Size(99, 14); + this.type.TabIndex = 1; + this.type.Text = "0 - Big Brown Pimp"; + // + // spritepanel + // + this.spritepanel.Controls.Add(this.spritename); + this.spritepanel.Controls.Add(this.spritetex); + this.spritepanel.Location = new System.Drawing.Point(479, 0); + this.spritepanel.Name = "spritepanel"; + this.spritepanel.Size = new System.Drawing.Size(107, 100); + this.spritepanel.TabIndex = 5; + this.spritepanel.TabStop = false; + this.spritepanel.Text = " Sprite "; + // + // spritename + // + this.spritename.Location = new System.Drawing.Point(11, 80); + this.spritename.Name = "spritename"; + this.spritename.Size = new System.Drawing.Size(84, 13); + this.spritename.TabIndex = 1; + this.spritename.Text = "BROWNHUG"; + this.spritename.TextAlign = System.Drawing.ContentAlignment.TopCenter; + // + // spritetex + // + this.spritetex.BackColor = System.Drawing.SystemColors.AppWorkspace; + this.spritetex.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Zoom; + this.spritetex.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D; + this.spritetex.Location = new System.Drawing.Point(19, 19); + this.spritetex.Name = "spritetex"; + this.spritetex.Size = new System.Drawing.Size(68, 60); + this.spritetex.TabIndex = 0; + // + // ThingInfoPanel + // + this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; + this.Controls.Add(this.spritepanel); + this.Controls.Add(this.infopanel); + this.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.MaximumSize = new System.Drawing.Size(10000, 100); + this.MinimumSize = new System.Drawing.Size(100, 100); + this.Name = "ThingInfoPanel"; + this.Size = new System.Drawing.Size(650, 100); + this.infopanel.ResumeLayout(false); + this.infopanel.PerformLayout(); + this.spritepanel.ResumeLayout(false); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.GroupBox spritepanel; + private System.Windows.Forms.Label spritename; + private System.Windows.Forms.Panel spritetex; + private System.Windows.Forms.Label angle; + private System.Windows.Forms.Label tag; + private System.Windows.Forms.Label position; + private System.Windows.Forms.Label action; + private System.Windows.Forms.Label type; + private System.Windows.Forms.Label arg5; + private System.Windows.Forms.Label arglbl5; + private System.Windows.Forms.Label arglbl4; + private System.Windows.Forms.Label arg4; + private System.Windows.Forms.Label arglbl3; + private System.Windows.Forms.Label arglbl2; + private System.Windows.Forms.Label arg3; + private System.Windows.Forms.Label arglbl1; + private System.Windows.Forms.Label arg2; + private System.Windows.Forms.Label arg1; + private System.Windows.Forms.GroupBox infopanel; + + } +} diff --git a/Source/Core/Controls/ThingInfoPanel.cs b/Source/Core/Controls/ThingInfoPanel.cs new file mode 100644 index 0000000..4fcafea --- /dev/null +++ b/Source/Core/Controls/ThingInfoPanel.cs @@ -0,0 +1,247 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Text; +using System.Windows.Forms; +using CodeImp.DoomBuilder.Geometry; +using Microsoft.Win32; +using System.Diagnostics; +using CodeImp.DoomBuilder.Data; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Config; +using CodeImp.DoomBuilder.Types; +using CodeImp.DoomBuilder.IO; + +#endregion + +namespace CodeImp.DoomBuilder.Controls +{ + internal partial class ThingInfoPanel : UserControl + { + private int hexenformatwidth; + private int doomformatwidth; + + // Constructor + public ThingInfoPanel() + { + // Initialize + InitializeComponent(); + + // Hide stuff when in Doom format + hexenformatwidth = infopanel.Width; + doomformatwidth = infopanel.Width - 190; + } + + // This shows the info + public void ShowInfo(Thing t) + { + ThingTypeInfo ti; + LinedefActionInfo act = null; + TypeHandler th; + string actioninfo = ""; + string zinfo; + float zvalue; + + // Show/hide stuff depending on format + if (!General.Map.FormatInterface.HasActionArgs) + { + arglbl1.Visible = false; + arglbl2.Visible = false; + arglbl3.Visible = false; + arglbl4.Visible = false; + arglbl5.Visible = false; + arg1.Visible = false; + arg2.Visible = false; + arg3.Visible = false; + arg4.Visible = false; + arg5.Visible = false; + infopanel.Width = doomformatwidth; + } + else + { + arglbl1.Visible = true; + arglbl2.Visible = true; + arglbl3.Visible = true; + arglbl4.Visible = true; + arglbl5.Visible = true; + arg1.Visible = true; + arg2.Visible = true; + arg3.Visible = true; + arg4.Visible = true; + arg5.Visible = true; + infopanel.Width = hexenformatwidth; + } + + // Move panel + spritepanel.Left = infopanel.Left + infopanel.Width + infopanel.Margin.Right + spritepanel.Margin.Left; + + // Lookup thing info + ti = General.Map.Data.GetThingInfo(t.Type); + + // Get thing action information + if (General.Map.Config.LinedefActions.ContainsKey(t.Action)) + { + act = General.Map.Config.LinedefActions[t.Action]; + actioninfo = act.ToString(); + } + else if (t.Action == 0) + actioninfo = t.Action.ToString() + " - None"; + else + actioninfo = t.Action.ToString() + " - Unknown"; + + // Determine z info to show + t.DetermineSector(); + if (ti.AbsoluteZ) + { + zvalue = t.Position.z; + zinfo = zvalue.ToString(); + } + else + { + if (t.Sector != null) + { + // Hangs from ceiling? + if (ti.Hangs) + { + zvalue = (float)t.Sector.CeilHeight + t.Position.z; + zinfo = zvalue.ToString(); + } + else + { + zvalue = (float)t.Sector.FloorHeight + t.Position.z; + zinfo = zvalue.ToString(); + } + } + else + { + zvalue = t.Position.z; + if (zvalue >= 0.0f) zinfo = "+" + zvalue.ToString(); else zinfo = zvalue.ToString(); + } + } + + // Thing info + infopanel.Text = " Thing " + t.Index + " "; + type.Text = t.Type + " - " + ti.Title; + action.Text = actioninfo; + position.Text = t.Position.x.ToString() + ", " + t.Position.y.ToString() + ", " + zinfo; + tag.Text = t.Tag.ToString(); + angle.Text = Angle2D.RealToDoom(t.Angle).ToString() + "\u00B0"; + + // Sprite + if (ti.Title == "Camera") // villsa + { + General.DisplayZoomedImage(spritetex, General.Map.Data.ThingCamera.GetBitmap()); + spritename.Text = ""; + } + else if (ti.Title == "Trigger") // villsa 9/11/11 + { + General.DisplayZoomedImage(spritetex, General.Map.Data.ThingTrigger.GetBitmap()); + spritename.Text = ""; + } + else if (ti.Sprite.ToLowerInvariant().StartsWith(DataManager.INTERNAL_PREFIX) && (ti.Sprite.Length > DataManager.INTERNAL_PREFIX.Length)) + { + spritename.Text = ""; + General.DisplayZoomedImage(spritetex, General.Map.Data.GetSpriteImage(ti.Sprite).GetBitmap()); + } + else if ((ti.Sprite.Length <= 8) && (ti.Sprite.Length > 0)) + { + spritename.Text = ti.Sprite; + General.DisplayZoomedImage(spritetex, General.Map.Data.GetSpriteImage(ti.Sprite).GetPreview()); + } + else + { + spritename.Text = ""; + spritetex.BackgroundImage = null; + } + + // Arguments + if (act != null) + { + arglbl1.Text = act.Args[0].Title + ":"; + arglbl2.Text = act.Args[1].Title + ":"; + arglbl3.Text = act.Args[2].Title + ":"; + arglbl4.Text = act.Args[3].Title + ":"; + arglbl5.Text = act.Args[4].Title + ":"; + arglbl1.Enabled = act.Args[0].Used; + arglbl2.Enabled = act.Args[1].Used; + arglbl3.Enabled = act.Args[2].Used; + arglbl4.Enabled = act.Args[3].Used; + arglbl5.Enabled = act.Args[4].Used; + arg1.Enabled = act.Args[0].Used; + arg2.Enabled = act.Args[1].Used; + arg3.Enabled = act.Args[2].Used; + arg4.Enabled = act.Args[3].Used; + arg5.Enabled = act.Args[4].Used; + th = General.Types.GetArgumentHandler(act.Args[0]); + th.SetValue(t.Args[0]); arg1.Text = th.GetStringValue(); + th = General.Types.GetArgumentHandler(act.Args[1]); + th.SetValue(t.Args[1]); arg2.Text = th.GetStringValue(); + th = General.Types.GetArgumentHandler(act.Args[2]); + th.SetValue(t.Args[2]); arg3.Text = th.GetStringValue(); + th = General.Types.GetArgumentHandler(act.Args[3]); + th.SetValue(t.Args[3]); arg4.Text = th.GetStringValue(); + th = General.Types.GetArgumentHandler(act.Args[4]); + th.SetValue(t.Args[4]); arg5.Text = th.GetStringValue(); + } + else + { + arglbl1.Text = "Argument 1:"; + arglbl2.Text = "Argument 2:"; + arglbl3.Text = "Argument 3:"; + arglbl4.Text = "Argument 4:"; + arglbl5.Text = "Argument 5:"; + arglbl1.Enabled = false; + arglbl2.Enabled = false; + arglbl3.Enabled = false; + arglbl4.Enabled = false; + arglbl5.Enabled = false; + arg1.Enabled = false; + arg2.Enabled = false; + arg3.Enabled = false; + arg4.Enabled = false; + arg5.Enabled = false; + arg1.Text = "-"; + arg2.Text = "-"; + arg3.Text = "-"; + arg4.Text = "-"; + arg5.Text = "-"; + } + + // Show the whole thing + this.Show(); + this.Update(); + } + + // When visible changed + protected override void OnVisibleChanged(EventArgs e) + { + // Hiding panels + if (!this.Visible) + { + spritetex.BackgroundImage = null; + } + + // Call base + base.OnVisibleChanged(e); + } + } +} diff --git a/Source/Core/Controls/ThingInfoPanel.resx b/Source/Core/Controls/ThingInfoPanel.resx new file mode 100644 index 0000000..85bf3e7 --- /dev/null +++ b/Source/Core/Controls/ThingInfoPanel.resx @@ -0,0 +1,210 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + True + + + False + + + True + + + False + + + True + + + False + + + True + + + False + + + True + + + False + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + \ No newline at end of file diff --git a/Source/Core/Controls/TransparentPanel.cs b/Source/Core/Controls/TransparentPanel.cs new file mode 100644 index 0000000..bb5dbcd --- /dev/null +++ b/Source/Core/Controls/TransparentPanel.cs @@ -0,0 +1,68 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Text; +using System.Globalization; +using System.Windows.Forms; +using CodeImp.DoomBuilder.Actions; +using CodeImp.DoomBuilder.Geometry; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Editing; + +#endregion + +namespace CodeImp.DoomBuilder.Controls +{ + public class TransparentPanel : Panel + { + #region ================== Constructor / Disposer + + // Constructor + public TransparentPanel() + { + } + + #endregion + + #region ================== Methods + + // Override this property + protected override CreateParams CreateParams + { + get + { + CreateParams cp = base.CreateParams; + cp.ExStyle |= 0x20; + return cp; + } + } + + // Disable background drawing by overriding this + protected override void OnPaintBackground(PaintEventArgs e) + { + if (BackColor != Color.Transparent) + e.Graphics.Clear(BackColor); + } + + #endregion + } +} diff --git a/Source/Core/Controls/TransparentTrackBar.cs b/Source/Core/Controls/TransparentTrackBar.cs new file mode 100644 index 0000000..0b8450a --- /dev/null +++ b/Source/Core/Controls/TransparentTrackBar.cs @@ -0,0 +1,41 @@ +using System.Drawing; +using System.Windows.Forms; + +// [ZZ] this is a copypasted version of TransparentPanel :) +// implements the same functionality, except for a TrackBar, for use in tab controls. + +namespace CodeImp.DoomBuilder.Controls +{ + public class TransparentTrackBar : TrackBar + { + #region ================== Constructor / Disposer + + // Constructor + public TransparentTrackBar() + { + SetStyle(ControlStyles.SupportsTransparentBackColor, true); + } + + #endregion + + #region ================== Methods + + protected override void OnCreateControl() + { + SetStyle(ControlStyles.SupportsTransparentBackColor, true); + if (Parent != null) + BackColor = Parent.BackColor; + + base.OnCreateControl(); + } + + // Disable background drawing by overriding this + protected override void OnPaintBackground(PaintEventArgs e) + { + if (BackColor != Color.Transparent) + e.Graphics.Clear(BackColor); + } + + #endregion + } +} diff --git a/Source/Core/Controls/VertexInfoPanel.Designer.cs b/Source/Core/Controls/VertexInfoPanel.Designer.cs new file mode 100644 index 0000000..03bbbb2 --- /dev/null +++ b/Source/Core/Controls/VertexInfoPanel.Designer.cs @@ -0,0 +1,89 @@ +namespace CodeImp.DoomBuilder.Controls +{ + partial class VertexInfoPanel + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.Windows.Forms.Label label1; + this.vertexinfo = new System.Windows.Forms.GroupBox(); + this.position = new System.Windows.Forms.Label(); + label1 = new System.Windows.Forms.Label(); + this.vertexinfo.SuspendLayout(); + this.SuspendLayout(); + // + // label1 + // + label1.AutoSize = true; + label1.Location = new System.Drawing.Point(13, 34); + label1.Name = "label1"; + label1.Size = new System.Drawing.Size(47, 14); + label1.TabIndex = 2; + label1.Text = "Position:"; + // + // vertexinfo + // + this.vertexinfo.Controls.Add(this.position); + this.vertexinfo.Controls.Add(label1); + this.vertexinfo.Location = new System.Drawing.Point(0, 0); + this.vertexinfo.Name = "vertexinfo"; + this.vertexinfo.Size = new System.Drawing.Size(163, 100); + this.vertexinfo.TabIndex = 0; + this.vertexinfo.TabStop = false; + this.vertexinfo.Text = " Vertex "; + // + // position + // + this.position.AutoSize = true; + this.position.Location = new System.Drawing.Point(66, 34); + this.position.Name = "position"; + this.position.Size = new System.Drawing.Size(25, 14); + this.position.TabIndex = 3; + this.position.Text = "0, 0"; + // + // VertexInfoPanel + // + this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; + this.Controls.Add(this.vertexinfo); + this.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.MaximumSize = new System.Drawing.Size(10000, 100); + this.MinimumSize = new System.Drawing.Size(100, 100); + this.Name = "VertexInfoPanel"; + this.Size = new System.Drawing.Size(393, 100); + this.vertexinfo.ResumeLayout(false); + this.vertexinfo.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.Label position; + private System.Windows.Forms.GroupBox vertexinfo; + + } +} diff --git a/Source/Core/Controls/VertexInfoPanel.cs b/Source/Core/Controls/VertexInfoPanel.cs new file mode 100644 index 0000000..82a877e --- /dev/null +++ b/Source/Core/Controls/VertexInfoPanel.cs @@ -0,0 +1,55 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Text; +using System.Windows.Forms; +using Microsoft.Win32; +using System.Diagnostics; +using CodeImp.DoomBuilder.Data; +using CodeImp.DoomBuilder.Map; + +#endregion + +namespace CodeImp.DoomBuilder.Controls +{ + internal partial class VertexInfoPanel : UserControl + { + // Constructor + public VertexInfoPanel() + { + // Initialize + InitializeComponent(); + } + + // This shows the info + public void ShowInfo(Vertex v) + { + // Vertex info + vertexinfo.Text = " Vertex " + v.Index + " "; + position.Text = v.Position.x.ToString("0.##") + ", " + v.Position.y.ToString("0.##"); + + // Show the whole thing + this.Show(); + this.Update(); + } + } +} diff --git a/Source/Core/Controls/VertexInfoPanel.resx b/Source/Core/Controls/VertexInfoPanel.resx new file mode 100644 index 0000000..af579e6 --- /dev/null +++ b/Source/Core/Controls/VertexInfoPanel.resx @@ -0,0 +1,135 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + True + + + False + + + True + + + True + + + True + + \ No newline at end of file diff --git a/Source/Core/Data/ColorImage.cs b/Source/Core/Data/ColorImage.cs new file mode 100644 index 0000000..0d9afd6 --- /dev/null +++ b/Source/Core/Data/ColorImage.cs @@ -0,0 +1,105 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Drawing; +using System.Drawing.Imaging; +using System.IO; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Rendering; + +#endregion + +namespace CodeImp.DoomBuilder.Data +{ + internal sealed unsafe class ColorImage : ImageData + { + #region ================== Variables + + private PixelColor color; + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + public ColorImage(PixelColor color, int width, int height) + { + // Initialize + this.width = width; + this.height = height; + this.color = color; + SetName(color.ToColorValue().ToString()); + + // We have no destructor + GC.SuppressFinalize(this); + } + + #endregion + + #region ================== Methods + + // This loads the image + protected override void LocalLoadImage() + { + // Leave when already loaded + if (this.IsImageLoaded) return; + if ((width == 0) || (height == 0)) return; + + lock (this) + { + // Create bitmap + try + { + if (bitmap != null) bitmap.Dispose(); + bitmap = new Bitmap(width, height, PixelFormat.Format32bppArgb); + BitmapData bitmapdata = bitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb); + PixelColor* pixels = (PixelColor*)bitmapdata.Scan0.ToPointer(); + for (int i = 0; i < (width * height); i++) + { + *pixels = color; + pixels++; + } + bitmap.UnlockBits(bitmapdata); + } + catch (Exception e) + { + // Unable to make bitmap + General.ErrorLogger.Add(ErrorType.Error, "Unable to create color image '" + this.Name + "'. " + e.GetType().Name + ": " + e.Message); + loadfailed = true; + } + + // Dispose bitmap if load failed + if (loadfailed && (bitmap != null)) + { + bitmap.Dispose(); + bitmap = null; + } + + // Pass on to base + base.LocalLoadImage(); + } + } + + #endregion + } +} diff --git a/Source/Core/Data/ColormapImage.cs b/Source/Core/Data/ColormapImage.cs new file mode 100644 index 0000000..0978fbb --- /dev/null +++ b/Source/Core/Data/ColormapImage.cs @@ -0,0 +1,121 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Drawing; +using System.Drawing.Imaging; +using System.IO; +using CodeImp.DoomBuilder.IO; + +#endregion + +namespace CodeImp.DoomBuilder.Data +{ + internal sealed class ColormapImage : ImageData + { + #region ================== Constructor / Disposer + + // Constructor + public ColormapImage(string name) + { + // Initialize + SetName(name); + + // We have no destructor + GC.SuppressFinalize(this); + } + + #endregion + + #region ================== Methods + + // This loads the image + protected override void LocalLoadImage() + { + Stream lumpdata; + MemoryStream mem; + IImageReader reader; + byte[] membytes; + + // Leave when already loaded + if (this.IsImageLoaded) return; + + lock (this) + { + // Get the lump data stream + lumpdata = General.Map.Data.GetColormapData(Name); + if (lumpdata != null) + { + // Copy lump data to memory + lumpdata.Seek(0, SeekOrigin.Begin); + membytes = new byte[(int)lumpdata.Length]; + lumpdata.Read(membytes, 0, (int)lumpdata.Length); + mem = new MemoryStream(membytes); + mem.Seek(0, SeekOrigin.Begin); + + // Get a reader for the data + reader = ImageDataFormat.GetImageReader(mem, ImageDataFormat.DOOMCOLORMAP, General.Map.Data.Palette); + if (reader is UnknownImageReader) + { + // Data is in an unknown format! + General.ErrorLogger.Add(ErrorType.Error, "Colormap lump '" + Name + "' data format could not be read. Does this lump contain valid colormap data at all?"); + bitmap = null; + } + else + { + // Read data as bitmap + mem.Seek(0, SeekOrigin.Begin); + if (bitmap != null) bitmap.Dispose(); + bitmap = reader.ReadAsBitmap(mem); + } + + // Done + mem.Dispose(); + + if (bitmap != null) + { + // Get width and height from image and set the scale + width = bitmap.Size.Width; + height = bitmap.Size.Height; + scale.x = General.Map.Config.DefaultFlatScale; + scale.y = General.Map.Config.DefaultFlatScale; + } + else + { + loadfailed = true; + } + } + else + { + // Missing a patch lump! + General.ErrorLogger.Add(ErrorType.Error, "Missing colormap lump '" + Name + "'. Did you forget to include required resources?"); + loadfailed = true; + } + + // Pass on to base + base.LocalLoadImage(); + } + } + + #endregion + } +} diff --git a/Source/Core/Data/DataLocation.cs b/Source/Core/Data/DataLocation.cs new file mode 100644 index 0000000..d95a999 --- /dev/null +++ b/Source/Core/Data/DataLocation.cs @@ -0,0 +1,79 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; + +#endregion + +namespace CodeImp.DoomBuilder.Data +{ + internal struct DataLocation : IComparable, IComparable, IEquatable + { + // Constants + public const int RESOURCE_WAD = 0; + public const int RESOURCE_DIRECTORY = 1; + public const int RESOURCE_PK3 = 2; + + // Members + public int type; + public string location; + public bool option1; + public bool option2; + public bool notfortesting; + + // Constructor + public DataLocation(int type, string location, bool option1, bool option2, bool notfortesting) + { + // Initialize + this.type = type; + this.location = location; + this.option1 = option1; + this.option2 = option2; + this.notfortesting = notfortesting; + } + + // This displays the struct as string + public override string ToString() + { + // Simply show location + return location; + } + + // This compares two locations + public int CompareTo(DataLocation other) + { + return string.Compare(this.location, other.location, true); + } + + // This compares two locations + public int CompareTo(object obj) + { + return string.Compare(this.location, ((DataLocation)obj).location, true); + } + + // This compares two locations + public bool Equals(DataLocation other) + { + return (this.CompareTo(other) == 0); + } + } +} diff --git a/Source/Core/Data/DataLocationList.cs b/Source/Core/Data/DataLocationList.cs new file mode 100644 index 0000000..b198ba2 --- /dev/null +++ b/Source/Core/Data/DataLocationList.cs @@ -0,0 +1,110 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using CodeImp.DoomBuilder.IO; +using System.Collections.Specialized; + +#endregion + +namespace CodeImp.DoomBuilder.Data +{ + internal sealed class DataLocationList : List + { + #region ================== Constructors + + // This creates a new list + public DataLocationList() + { + } + + // This creates a list from a configuration structure + public DataLocationList(Configuration cfg, string path) + { + IDictionary resinfo, rlinfo; + DataLocation res; + + // Go for all items in the map info + resinfo = cfg.ReadSetting(path, new ListDictionary()); + foreach (DictionaryEntry rl in resinfo) + { + // Item is a structure? + if (rl.Value is IDictionary) + { + // Create resource location + rlinfo = (IDictionary)rl.Value; + res = new DataLocation(); + + // Copy information from Configuration to ResourceLocation + if (rlinfo.Contains("type") && (rlinfo["type"] is int)) res.type = (int)rlinfo["type"]; + if (rlinfo.Contains("location") && (rlinfo["location"] is string)) res.location = (string)rlinfo["location"]; + if (rlinfo.Contains("option1") && (rlinfo["option1"] is int)) res.option1 = General.Int2Bool((int)rlinfo["option1"]); + if (rlinfo.Contains("option2") && (rlinfo["option2"] is int)) res.option2 = General.Int2Bool((int)rlinfo["option2"]); + if (rlinfo.Contains("notfortesting") && (rlinfo["notfortesting"] is int)) res.notfortesting = General.Int2Bool((int)rlinfo["notfortesting"]); + + // Add resource + Add(res); + } + } + } + + #endregion + + #region ================== Methods + + // This merges two lists together + public static DataLocationList Combined(DataLocationList a, DataLocationList b) + { + DataLocationList result = new DataLocationList(); + result.AddRange(a); + result.AddRange(b); + return result; + } + + // This writes the list to configuration + public void WriteToConfig(Configuration cfg, string path) + { + IDictionary resinfo, rlinfo; + + // Fill structure + resinfo = new ListDictionary(); + for (int i = 0; i < this.Count; i++) + { + // Create structure for resource + rlinfo = new ListDictionary(); + rlinfo.Add("type", this[i].type); + rlinfo.Add("location", this[i].location); + rlinfo.Add("option1", General.Bool2Int(this[i].option1)); + rlinfo.Add("option2", General.Bool2Int(this[i].option2)); + rlinfo.Add("notfortesting", General.Bool2Int(this[i].notfortesting)); + + // Add structure + resinfo.Add("resource" + i.ToString(CultureInfo.InvariantCulture), rlinfo); + } + + // Write to config + cfg.WriteSetting(path, resinfo); + } + + #endregion + } +} diff --git a/Source/Core/Data/DataManager.cs b/Source/Core/Data/DataManager.cs new file mode 100644 index 0000000..ee2ce5d --- /dev/null +++ b/Source/Core/Data/DataManager.cs @@ -0,0 +1,1413 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Drawing; +using System.Drawing.Imaging; +using System.IO; +using CodeImp.DoomBuilder.IO; +using System.Windows.Forms; +using SlimDX.Direct3D9; +using CodeImp.DoomBuilder.Config; +using System.Threading; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Windows; +using CodeImp.DoomBuilder.ZDoom; + +#endregion + +namespace CodeImp.DoomBuilder.Data +{ + public sealed class DataManager + { + #region ================== Constants + + public const string INTERNAL_PREFIX = "internal:"; + + #endregion + + #region ================== Variables + + // Data containers + private List containers; + private DataReader currentreader; + + // Palette + private Playpal palette; + + // villsa - thing palettes + private Dictionary thingpalettes; + + // Textures, Flats and Sprites + private Dictionary textures; + private List texturenames; + private Dictionary flats; + private List flatnames; + private Dictionary sprites; + private List texturesets; + private List resourcetextures; + private AllTextureSet alltextures; + + // Background loading + private Queue imageque; + private Thread backgroundloader; + private volatile bool updatedusedtextures; + private bool notifiedbusy; + + // Image previews + private PreviewManager previews; + + // Special images + private ImageData thingcamera; // villsa 9/11/11 + private ImageData thingtrigger; // villsa 9/11/11 + private ImageData missingtexture3d; + private ImageData unknowntexture3d; + private ImageData hourglass3d; + private ImageData crosshair; + private ImageData crosshairbusy; + private Dictionary internalsprites; + private ImageData thingbox; + private ImageData whitetexture; + + // Used images + private Dictionary usedimages; + + // Things combined with things created from Decorate + private DecorateParser decorate; + private List thingcategories; + private Dictionary thingtypes; + + // Timing + private double loadstarttime; + private double loadfinishtime; + + // Disposing + private bool isdisposed = false; + + #endregion + + #region ================== Properties + + public Playpal Palette { get { return palette; } } + public IDictionary ThingPalette { get { return thingpalettes; } } // villsa + public PreviewManager Previews { get { return previews; } } + public ICollection Textures { get { return textures.Values; } } + public ICollection Flats { get { return flats.Values; } } + public List TextureNames { get { return texturenames; } } + public List FlatNames { get { return flatnames; } } + public bool IsDisposed { get { return isdisposed; } } + public ImageData ThingCamera { get { return thingcamera; } } // villsa 9/11/11 + public ImageData ThingTrigger { get { return thingtrigger; } } // villsa 9/11/11 + public ImageData MissingTexture3D { get { return missingtexture3d; } } + public ImageData UnknownTexture3D { get { return unknowntexture3d; } } + public ImageData Hourglass3D { get { return hourglass3d; } } + public ImageData Crosshair3D { get { return crosshair; } } + public ImageData CrosshairBusy3D { get { return crosshairbusy; } } + public ImageData ThingBox { get { return thingbox; } } + public ImageData WhiteTexture { get { return whitetexture; } } + public List ThingCategories { get { return thingcategories; } } + public ICollection ThingTypes { get { return thingtypes.Values; } } + public DecorateParser Decorate { get { return decorate; } } + internal ICollection TextureSets { get { return texturesets; } } + internal ICollection ResourceTextureSets { get { return resourcetextures; } } + internal AllTextureSet AllTextureSet { get { return alltextures; } } + + public bool IsLoading + { + get + { + if (imageque != null) + { + return (backgroundloader != null) && backgroundloader.IsAlive && ((imageque.Count > 0) || previews.IsLoading); + } + else + { + return false; + } + } + } + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + internal DataManager() + { + // We have no destructor + GC.SuppressFinalize(this); + + // Load special images + missingtexture3d = new ResourceImage("CodeImp.DoomBuilder.Resources.MissingTexture3D.png"); + missingtexture3d.LoadImage(); + unknowntexture3d = new ResourceImage("CodeImp.DoomBuilder.Resources.UnknownTexture3D.png"); + unknowntexture3d.LoadImage(); + hourglass3d = new ResourceImage("CodeImp.DoomBuilder.Resources.Hourglass3D.png"); + hourglass3d.LoadImage(); + thingcamera = new ResourceImage("CodeImp.DoomBuilder.Resources.ThingCamera.png"); // villsa 9/11/11 + thingcamera.LoadImage(); // villsa 9/11/11 + thingtrigger = new ResourceImage("CodeImp.DoomBuilder.Resources.ThingTrigger.png"); // villsa 9/11/11 + thingtrigger.LoadImage(); // villsa 9/11/11 + crosshair = new ResourceImage("CodeImp.DoomBuilder.Resources.Crosshair.png"); + crosshair.LoadImage(); + crosshairbusy = new ResourceImage("CodeImp.DoomBuilder.Resources.CrosshairBusy.png"); + crosshairbusy.LoadImage(); + thingbox = new ResourceImage("CodeImp.DoomBuilder.Resources.ThingBox.png"); + thingbox.LoadImage(); + whitetexture = new ResourceImage("CodeImp.DoomBuilder.Resources.White.png"); + whitetexture.UseColorCorrection = false; + whitetexture.LoadImage(); + whitetexture.CreateTexture(); + } + + // Disposer + internal void Dispose() + { + // Not already disposed? + if (!isdisposed) + { + // Clean up + Unload(); + thingcamera.Dispose(); // villsa 9/11/11 + thingcamera = null; // villsa 9/11/11 + thingtrigger.Dispose(); // villsa 9/11/11 + thingtrigger = null; // villsa 9/11/11 + missingtexture3d.Dispose(); + missingtexture3d = null; + unknowntexture3d.Dispose(); + unknowntexture3d = null; + hourglass3d.Dispose(); + hourglass3d = null; + crosshair.Dispose(); + crosshair = null; + crosshairbusy.Dispose(); + crosshairbusy = null; + thingbox.Dispose(); + thingbox = null; + whitetexture.Dispose(); + whitetexture = null; + + // Done + isdisposed = true; + } + } + + #endregion + + #region ================== Loading / Unloading + + // This loads all data resources + internal void Load(DataLocationList configlist, DataLocationList maplist, DataLocation maplocation) + { + DataLocationList all = DataLocationList.Combined(configlist, maplist); + all.Add(maplocation); + Load(all); + } + + // This loads all data resources + internal void Load(DataLocationList configlist, DataLocationList maplist) + { + DataLocationList all = DataLocationList.Combined(configlist, maplist); + Load(all); + } + + // This loads all data resources + internal void Load(DataLocationList locations) + { + int texcount, flatcount, spritecount, thingcount, colormapcount; + Dictionary texturesonly = new Dictionary(); + Dictionary colormapsonly = new Dictionary(); + Dictionary flatsonly = new Dictionary(); + DataReader c; + + // Create collections + containers = new List(); + textures = new Dictionary(); + thingpalettes = new Dictionary(); + flats = new Dictionary(); + sprites = new Dictionary(); + texturenames = new List(); + flatnames = new List(); + imageque = new Queue(); + previews = new PreviewManager(); + texturesets = new List(); + usedimages = new Dictionary(); + internalsprites = new Dictionary(); + thingcategories = General.Map.Config.GetThingCategories(); + thingtypes = General.Map.Config.GetThingTypes(); + + // Load texture sets + foreach (DefinedTextureSet ts in General.Map.ConfigSettings.TextureSets) + texturesets.Add(new MatchingTextureSet(ts)); + + // Sort the texture sets + texturesets.Sort(); + + // Special textures sets + alltextures = new AllTextureSet(); + resourcetextures = new List(); + + // Go for all locations + foreach (DataLocation dl in locations) + { + // Nothing chosen yet + c = null; + + // TODO: Make this work more elegant using reflection. + // Make DataLocation.type of type Type and assign the + // types of the desired reader classes. + + try + { + // Choose container type + switch (dl.type) + { + // WAD file container + case DataLocation.RESOURCE_WAD: + c = new WADReader(dl); + break; + + // Directory container + case DataLocation.RESOURCE_DIRECTORY: + c = new DirectoryReader(dl); + break; + + // PK3 file container + case DataLocation.RESOURCE_PK3: + c = new PK3Reader(dl); + break; + } + } + catch (Exception e) + { + // Unable to load resource + General.ErrorLogger.Add(ErrorType.Error, "Unable to load resources from location \"" + dl.location + "\". Please make sure the location is accessible and not in use by another program. The resources will now be loaded with this location excluded. You may reload the resources to try again.\n" + e.GetType().Name + " when creating data reader: " + e.Message + ")"); + General.WriteLogLine(e.StackTrace); + continue; + } + + // Add container + if (c != null) + { + containers.Add(c); + resourcetextures.Add(c.TextureSet); + } + } + + // Load stuff + LoadPalette(); + texcount = LoadTextures(texturesonly); + flatcount = LoadFlats(flatsonly); + colormapcount = LoadColormaps(colormapsonly); + thingcount = LoadDecorateThings(); + spritecount = LoadSprites(); + LoadInternalSprites(); + + foreach (TextureIndexInfo tp in General.Map.Config.ThingPalettes) + { + LoadThingPalette(tp.Title); + } + + // Process colormaps (we just put them in as textures) + foreach (KeyValuePair t in colormapsonly) + { + textures.Add(t.Key, t.Value); + texturenames.Add(t.Value.Name); + } + + // Process textures + foreach (KeyValuePair t in texturesonly) + { + if (!textures.ContainsKey(t.Key)) + { + textures.Add(t.Key, t.Value); + texturenames.Add(t.Value.Name); + } + } + + // Process flats + foreach (KeyValuePair f in flatsonly) + { + flats.Add(f.Key, f.Value); + flatnames.Add(f.Value.Name); + } + + // Mixed textures and flats? + if (General.Map.Config.MixTexturesFlats) + { + // Add textures to flats + foreach (KeyValuePair t in texturesonly) + { + if (!flats.ContainsKey(t.Key)) + { + flats.Add(t.Key, t.Value); + flatnames.Add(t.Value.Name); + } + } + + // Add flats to textures + foreach (KeyValuePair f in flatsonly) + { + if (!textures.ContainsKey(f.Key)) + { + textures.Add(f.Key, f.Value); + texturenames.Add(f.Value.Name); + } + } + + // Do the same on the data readers + foreach (DataReader dr in containers) + dr.TextureSet.MixTexturesAndFlats(); + } + + // Sort names + texturenames.Sort(); + flatnames.Sort(); + + // Sort things + foreach (ThingCategory tc in thingcategories) tc.SortIfNeeded(); + + // Update the used textures + General.Map.Data.UpdateUsedTextures(); + + // Add texture names to texture sets + foreach (KeyValuePair img in textures) + { + // Add to all sets where it matches + bool matchfound = false; + foreach (MatchingTextureSet ms in texturesets) + matchfound |= ms.AddTexture(img.Value); + + // Add to all + alltextures.AddTexture(img.Value); + } + + // Add flat names to texture sets + foreach (KeyValuePair img in flats) + { + // Add to all sets where it matches + bool matchfound = false; + foreach (MatchingTextureSet ms in texturesets) + matchfound |= ms.AddFlat(img.Value); + + // Add to all + alltextures.AddFlat(img.Value); + } + + // Start background loading + StartBackgroundLoader(); + + // Output info + General.WriteLogLine("Loaded " + texcount + " textures, " + flatcount + " flats, " + colormapcount + " colormaps, " + spritecount + " sprites, " + thingcount + " decorate things"); + } + + // This unloads all data + internal void Unload() + { + // Stop background loader + StopBackgroundLoader(); + + // Dispose preview manager + previews.Dispose(); + previews = null; + + // Dispose resources + foreach (KeyValuePair i in textures) i.Value.Dispose(); + foreach (KeyValuePair i in flats) i.Value.Dispose(); + foreach (KeyValuePair i in sprites) i.Value.Dispose(); + palette = null; + + // Dispose containers + foreach (DataReader c in containers) c.Dispose(); + containers.Clear(); + + // Trash collections + containers = null; + textures = null; + thingpalettes = null; // villsa + flats = null; + sprites = null; + texturenames = null; + flatnames = null; + imageque = null; + internalsprites = null; + } + + #endregion + + #region ================== Suspend / Resume + + // This suspends data resources + internal void Suspend() + { + // Stop background loader + StopBackgroundLoader(); + + // Go for all containers + foreach (DataReader d in containers) + { + // Suspend + General.WriteLogLine("Suspended data resource '" + d.Location.location + "'"); + d.Suspend(); + } + } + + // This resumes data resources + internal void Resume() + { + // Go for all containers + foreach (DataReader d in containers) + { + try + { + // Resume + General.WriteLogLine("Resumed data resource '" + d.Location.location + "'"); + d.Resume(); + } + catch (Exception e) + { + // Unable to load resource + General.ErrorLogger.Add(ErrorType.Error, "Unable to load resources from location \"" + d.Location.location + "\". Please make sure the location is accessible and not in use by another program. The resources will now be loaded with this location excluded. You may reload the resources to try again.\n" + e.GetType().Name + " when resuming data reader: " + e.Message + ")"); + General.WriteLogLine(e.StackTrace); + } + } + + // Start background loading + StartBackgroundLoader(); + } + + #endregion + + #region ================== Background Loading + + // This starts background loading + private void StartBackgroundLoader() + { + // Timing + loadstarttime = General.stopwatch.Elapsed.TotalMilliseconds; + loadfinishtime = 0; + + // If a loader is already running, stop it first + if (backgroundloader != null) StopBackgroundLoader(); + + // Start a low priority thread to load images in background + General.WriteLogLine("Starting background resource loading..."); + backgroundloader = new Thread(new ThreadStart(BackgroundLoad)); + backgroundloader.Name = "Background Loader"; + backgroundloader.Priority = ThreadPriority.Lowest; + backgroundloader.IsBackground = true; + backgroundloader.Start(); + } + + // This stops background loading + private void StopBackgroundLoader() + { + ImageData img; + + General.WriteLogLine("Stopping background resource loading..."); + if (backgroundloader != null) + { + // Stop the thread and wait for it to end + backgroundloader.Interrupt(); + backgroundloader.Join(); + + // Reset load states on all images in the list + while (imageque.Count > 0) + { + img = imageque.Dequeue(); + + switch (img.ImageState) + { + case ImageLoadState.Loading: + img.ImageState = ImageLoadState.None; + break; + + case ImageLoadState.Unloading: + img.ImageState = ImageLoadState.Ready; + break; + } + + switch (img.PreviewState) + { + case ImageLoadState.Loading: + img.PreviewState = ImageLoadState.None; + break; + + case ImageLoadState.Unloading: + img.PreviewState = ImageLoadState.Ready; + break; + } + } + + // Done + notifiedbusy = false; + backgroundloader = null; + General.SendMessage(General.MainWindow.Handle, (int)MainForm.ThreadMessages.UpdateStatus, 0, 0); + } + } + + // The background loader + private void BackgroundLoad() + { + try + { + do + { + // Do we have to update the used-in-map status? + if (updatedusedtextures) BackgroundUpdateUsedTextures(); + + // Get next item + ImageData image = null; + lock (imageque) + { + // Fetch next image to process + if (imageque.Count > 0) image = imageque.Dequeue(); + } + + // Any image to process? + if (image != null) + { + // Load this image? + if (image.IsReferenced && (image.ImageState != ImageLoadState.Ready)) + { + image.LoadImage(); + } + + // Unload this image? + if (!image.IsReferenced && image.AllowUnload && (image.ImageState != ImageLoadState.None)) + { + // Still unreferenced? + image.UnloadImage(); + } + } + + // Doing something? + if (image != null) + { + // Wait a bit and update icon + if (!notifiedbusy) + { + notifiedbusy = true; + General.SendMessage(General.MainWindow.Handle, (int)MainForm.ThreadMessages.UpdateStatus, 0, 0); + } + Thread.Sleep(0); + } + else + { + // Process previews only when we don't have images to process + // because these are lower priority than the actual images + if (previews.BackgroundLoad()) + { + // Wait a bit and update icon + if (!notifiedbusy) + { + notifiedbusy = true; + General.SendMessage(General.MainWindow.Handle, (int)MainForm.ThreadMessages.UpdateStatus, 0, 0); + } + Thread.Sleep(0); + } + else + { + if (notifiedbusy) + { + notifiedbusy = false; + General.SendMessage(General.MainWindow.Handle, (int)MainForm.ThreadMessages.UpdateStatus, 0, 0); + } + + // Timing + if (loadfinishtime == 0) + { + loadfinishtime = General.stopwatch.Elapsed.TotalMilliseconds; + double deltatimesec = (loadfinishtime - loadstarttime) / 1000.0d; + General.WriteLogLine("Resources loading took " + deltatimesec.ToString("########0.00") + " seconds"); + } + + // Wait longer to release CPU resources + Thread.Sleep(50); + } + } + } + while (true); + } + catch (ThreadInterruptedException) + { + return; + } + } + + // This adds an image for background loading or unloading + internal void ProcessImage(ImageData img) + { + // Load this image? + if ((img.ImageState == ImageLoadState.None) && img.IsReferenced) + { + // Add for loading + img.ImageState = ImageLoadState.Loading; + lock (imageque) { imageque.Enqueue(img); } + } + + // Unload this image? + if ((img.ImageState == ImageLoadState.Ready) && !img.IsReferenced && img.AllowUnload) + { + // Add for unloading + img.ImageState = ImageLoadState.Unloading; + lock (imageque) { imageque.Enqueue(img); } + } + + // Update icon + General.SendMessage(General.MainWindow.Handle, (int)MainForm.ThreadMessages.UpdateStatus, 0, 0); + } + + // This updates the used-in-map status on all textures and flats + private void BackgroundUpdateUsedTextures() + { + lock (usedimages) + { + // Set used on all textures + foreach (KeyValuePair i in textures) + { + i.Value.SetUsedInMap(usedimages.ContainsKey(i.Key)); + if (i.Value.IsImageLoaded != i.Value.IsReferenced) ProcessImage(i.Value); + } + + // Set used on all flats + foreach (KeyValuePair i in flats) + { + i.Value.SetUsedInMap(usedimages.ContainsKey(i.Key)); + if (i.Value.IsImageLoaded != i.Value.IsReferenced) ProcessImage(i.Value); + } + + // Done + updatedusedtextures = false; + } + } + + #endregion + + #region ================== Palette + + // This loads the PLAYPAL palette + private void LoadPalette() + { + // Go for all opened containers + for (int i = containers.Count - 1; i >= 0; i--) + { + // Load palette + palette = containers[i].LoadPalette(); + if (palette != null) break; + } + + // Make empty palette when still no palette found + if (palette == null) + { + General.ErrorLogger.Add(ErrorType.Warning, "None of the loaded resources define a color palette. Did you forget to configure an IWAD for this game configuration?"); + palette = new Playpal(); + } + } + + // villsa + private void LoadThingPalette(string name) + { + Playpal pal; + // Go for all opened containers + for (int i = containers.Count - 1; i >= 0; i--) + { + // Load palette + pal = containers[i].LoadThingPalette(name); + if (pal != null) + { + thingpalettes.Add(name, pal); + return; + } + } + } + + #endregion + + #region ================== Colormaps + + // This loads the colormaps + private int LoadColormaps(Dictionary list) + { + ICollection images; + int counter = 0; + + // Go for all opened containers + foreach (DataReader dr in containers) + { + // Load colormaps + images = dr.LoadColormaps(); + if (images != null) + { + // Go for all colormaps + foreach (ImageData img in images) + { + // Add or replace in flats list + list.Remove(img.LongName); + list.Add(img.LongName, img); + counter++; + + // Add to preview manager + previews.AddImage(img); + } + } + } + + // Output info + return counter; + } + + // This returns a specific colormap stream + internal Stream GetColormapData(string pname) + { + Stream colormap; + + // Go for all opened containers + for (int i = containers.Count - 1; i >= 0; i--) + { + // This contain provides this flat? + colormap = containers[i].GetColormapData(pname); + if (colormap != null) return colormap; + } + + // No such patch found + return null; + } + + #endregion + + #region ================== Textures + + // This loads the textures + private int LoadTextures(Dictionary list) + { + ICollection images; + PatchNames pnames = new PatchNames(); + PatchNames newpnames; + int counter = 0; + long firsttexture = 0; + + // Go for all opened containers + foreach (DataReader dr in containers) + { + // Load PNAMES info + // Note that pnames is NOT set to null in the loop + // because if a container has no pnames, the pnames + // of the previous (higher) container should be used. + newpnames = dr.LoadPatchNames(); + if (newpnames != null) pnames = newpnames; + + // Load textures + images = dr.LoadTextures(pnames); + if (images != null) + { + // Go for all textures + foreach (ImageData img in images) + { + // Add or replace in textures list + list.Remove(img.LongName); + list.Add(img.LongName, img); + if (firsttexture == 0) firsttexture = img.LongName; + counter++; + + // Add to preview manager + previews.AddImage(img); + } + } + } + + // The first texture cannot be used, because in the game engine it + // has index 0 which means "no texture", so remove it from the list. + list.Remove(firsttexture); + + // Output info + return counter; + } + + // This returns a specific patch stream + internal Stream GetPatchData(string pname) + { + Stream patch; + + // Go for all opened containers + for (int i = containers.Count - 1; i >= 0; i--) + { + // This contain provides this patch? + patch = containers[i].GetPatchData(pname); + if (patch != null) return patch; + } + + // No such patch found + return null; + } + + // This returns a specific texture stream + internal Stream GetTextureData(string pname) + { + Stream patch; + + // Go for all opened containers + for (int i = containers.Count - 1; i >= 0; i--) + { + // This contain provides this patch? + patch = containers[i].GetTextureData(pname); + if (patch != null) return patch; + } + + // No such patch found + return null; + } + + // This checks if a given texture is known + public bool GetTextureExists(string name) + { + long longname = Lump.MakeLongName(name); + return textures.ContainsKey(longname); + } + + // This checks if a given texture is known + public bool GetTextureExists(long longname) + { + return textures.ContainsKey(longname); + } + + // This returns an image by string + public ImageData GetTextureImage(string name) + { + // Get the long name + long longname = Lump.MakeLongName(name); + return GetTextureImage(longname); + } + + // This returns an image by long + public ImageData GetTextureImage(long longname) + { + // Does this texture exist? + if (textures.ContainsKey(longname)) + { + // Return texture + return textures[longname]; + } + else + { + // Return null image + return new UnknownImage(Properties.Resources.UnknownImage); + } + } + + #endregion + + #region ================== Flats + + // This loads the flats + private int LoadFlats(Dictionary list) + { + ICollection images; + int counter = 0; + + // Go for all opened containers + foreach (DataReader dr in containers) + { + // Load flats + images = dr.LoadFlats(); + if (images != null) + { + // Go for all flats + foreach (ImageData img in images) + { + // Add or replace in flats list + list.Remove(img.LongName); + list.Add(img.LongName, img); + counter++; + + // Add to preview manager + previews.AddImage(img); + } + } + } + + // Output info + return counter; + } + + // This returns a specific flat stream + internal Stream GetFlatData(string pname) + { + Stream flat; + + // Go for all opened containers + for (int i = containers.Count - 1; i >= 0; i--) + { + // This contain provides this flat? + flat = containers[i].GetFlatData(pname); + if (flat != null) return flat; + } + + // No such patch found + return null; + } + + // This checks if a flat is known + public bool GetFlatExists(string name) + { + long longname = Lump.MakeLongName(name); + return flats.ContainsKey(longname); + } + + // This checks if a flat is known + public bool GetFlatExists(long longname) + { + return flats.ContainsKey(longname); + } + + // This returns an image by string + public ImageData GetFlatImage(string name) + { + // Get the long name + long longname = Lump.MakeLongName(name); + return GetFlatImage(longname); + } + + // This returns an image by long + public ImageData GetFlatImage(long longname) + { + // Does this flat exist? + if (flats.ContainsKey(longname)) + { + // Return flat + return flats[longname]; + } + else + { + // Return null image + return new UnknownImage(Properties.Resources.UnknownImage); + } + } + + // This returns an image by long and doesn't check if it exists + public ImageData GetFlatImageKnown(long longname) + { + // Return flat + return flats[longname]; + } + + #endregion + + #region ================== Sprites + + // This loads the sprites + private int LoadSprites() + { + // Go for all things + foreach (ThingTypeInfo ti in General.Map.Data.ThingTypes) + { + // Sprite not added to collection yet? + if (!sprites.ContainsKey(ti.SpriteLongName) && (ti.Sprite.Length <= 8)) + { + // Find sprite data + Stream spritedata = GetSpriteData(ti.Sprite); + if (spritedata != null) + { + // Make new sprite image + SpriteImage image = new SpriteImage(ti.Sprite); + + // Add to collection + sprites.Add(ti.SpriteLongName, image); + + // Add to preview manager + previews.AddImage(image); + + // villsa + if (ti.PalIndex > 0) + image.PalIndex = ti.PalIndex; + } + } + } + + // Output info + return sprites.Count; + } + + // This returns a specific patch stream + internal Stream GetSpriteData(string pname) + { + if (!string.IsNullOrEmpty(pname)) + { + // Go for all opened containers + for (int i = containers.Count - 1; i >= 0; i--) + { + // This contain provides this patch? + Stream spritedata = containers[i].GetSpriteData(pname); + if (spritedata != null) return spritedata; + } + } + + // No such patch found + return null; + } + + // This tests if a given sprite can be found + internal bool GetSpriteExists(string pname) + { + if (!string.IsNullOrEmpty(pname)) + { + // Go for all opened containers + for (int i = containers.Count - 1; i >= 0; i--) + { + // This contain provides this patch? + if (containers[i].GetSpriteExists(pname)) return true; + } + } + + // No such patch found + return false; + } + + // This loads the internal sprites + private void LoadInternalSprites() + { + // Add sprite icon files from directory + string[] files = Directory.GetFiles(General.SpritesPath, "*.png", SearchOption.TopDirectoryOnly); + foreach (string spritefile in files) + { + ImageData img = new FileImage(Path.GetFileNameWithoutExtension(spritefile).ToLowerInvariant(), spritefile, false); + img.LoadImage(); + img.AllowUnload = false; + internalsprites.Add(img.Name, img); + } + + // Add some internal resources + if (!internalsprites.ContainsKey("nothing")) + { + ImageData img = new ResourceImage("CodeImp.DoomBuilder.Resources.Nothing.png"); + img.LoadImage(); + img.AllowUnload = false; + internalsprites.Add("nothing", img); + } + + if (!internalsprites.ContainsKey("unknownthing")) + { + ImageData img = new ResourceImage("CodeImp.DoomBuilder.Resources.UnknownThing.png"); + img.LoadImage(); + img.AllowUnload = false; + internalsprites.Add("unknownthing", img); + } + } + + // This returns an image by long + public ImageData GetSpriteImage(string name) + { + // Is this referring to an internal sprite image? + if ((name.Length > INTERNAL_PREFIX.Length) && name.ToLowerInvariant().StartsWith(INTERNAL_PREFIX)) + { + // Get the internal sprite + string internalname = name.Substring(INTERNAL_PREFIX.Length).ToLowerInvariant(); + if (internalsprites.ContainsKey(internalname)) + { + return internalsprites[internalname]; + } + else + { + return new UnknownImage(Properties.Resources.UnknownImage); + } + } + else + { + // Get the long name + long longname = Lump.MakeLongName(name); + + // Sprite already loaded? + if (sprites.ContainsKey(longname)) + { + // Return exiting sprite + return sprites[longname]; + } + else + { + Stream spritedata = null; + + // Go for all opened containers + for (int i = containers.Count - 1; i >= 0; i--) + { + // This contain provides this sprite? + spritedata = containers[i].GetSpriteData(name); + if (spritedata != null) break; + } + + // Found anything? + if (spritedata != null) + { + // Make new sprite image + SpriteImage image = new SpriteImage(name); + + // Add to collection + sprites.Add(longname, image); + + // Return result + return image; + } + else + { + // Return null image + return new UnknownImage(Properties.Resources.UnknownImage); + } + } + } + } + + // BAD! These block while loading the image. That is not + // what our background loading system is for! + /* + // This returns a bitmap by string + public Bitmap GetSpriteBitmap(string name) + { + ImageData img = GetSpriteImage(name); + img.LoadImage(); + return img.Bitmap; + } + + // This returns a texture by string + public Texture GetSpriteTexture(string name) + { + ImageData img = GetSpriteImage(name); + img.LoadImage(); + img.CreateTexture(); + return img.Texture; + } + */ + + #endregion + + #region ================== Things + + // This loads the things from Decorate + private int LoadDecorateThings() + { + int counter = 0; + + // Create new parser + decorate = new DecorateParser(); + decorate.OnInclude = LoadDecorateFromLocation; + + // Only load these when the game configuration supports the use of decorate + if (!string.IsNullOrEmpty(General.Map.Config.DecorateGames)) + { + // Go for all opened containers + foreach (DataReader dr in containers) + { + // Load Decorate info cumulatively (the last Decorate is added to the previous) + // I'm not sure if this is the right thing to do though. + currentreader = dr; + List decostreams = dr.GetDecorateData("DECORATE"); + foreach (Stream decodata in decostreams) + { + // Parse the data + decodata.Seek(0, SeekOrigin.Begin); + decorate.Parse(decodata, "DECORATE"); + + // Check for errors + if (decorate.HasError) + { + General.ErrorLogger.Add(ErrorType.Error, "Unable to parse DECORATE data from location " + + dr.Location.location + ". " + decorate.ErrorDescription + " on line " + decorate.ErrorLine + + " in '" + decorate.ErrorSource + "'"); + break; + } + } + } + + currentreader = null; + + if (!decorate.HasError) + { + // Go for all actors in the decorate to make things or update things + foreach (ActorStructure actor in decorate.Actors) + { + // Check if we want to add this actor + if (actor.DoomEdNum > 0) + { + string catname = actor.GetPropertyAllValues("$category").ToLowerInvariant(); + if (string.IsNullOrEmpty(catname.Trim())) catname = "decorate"; + + // Check if we can find this thing in our existing collection + if (thingtypes.ContainsKey(actor.DoomEdNum)) + { + // Update the thing + thingtypes[actor.DoomEdNum].ModifyByDecorateActor(actor); + } + else + { + // Find the category to put the actor in + // First search by Title, then search by Name + ThingCategory cat = null; + foreach (ThingCategory c in thingcategories) + { + if (c.Title.ToLowerInvariant() == catname) cat = c; + } + if (cat == null) + { + foreach (ThingCategory c in thingcategories) + { + if (c.Name.ToLowerInvariant() == catname) cat = c; + } + } + + // Make the category if needed + if (cat == null) + { + string catfullname = actor.GetPropertyAllValues("$category"); + if (string.IsNullOrEmpty(catfullname.Trim())) catfullname = "Decorate"; + cat = new ThingCategory(catname, catfullname); + thingcategories.Add(cat); + } + + // Add new thing + ThingTypeInfo t = new ThingTypeInfo(cat, actor); + cat.AddThing(t); + thingtypes.Add(t.Index, t); + } + + // Count + counter++; + } + } + } + } + + // Output info + return counter; + } + + // This loads Decorate data from a specific file or lump name + private void LoadDecorateFromLocation(DecorateParser parser, string location) + { + //General.WriteLogLine("Including DECORATE resource '" + location + "'..."); + List decostreams = currentreader.GetDecorateData(location); + foreach (Stream decodata in decostreams) + { + // Parse this data + parser.Parse(decodata, location); + } + } + + // This gets thing information by index + public ThingTypeInfo GetThingInfo(int thingtype) + { + // Index in config? + if (thingtypes.ContainsKey(thingtype)) + { + // Return from config + return thingtypes[thingtype]; + } + else + { + // Create unknown thing info + return new ThingTypeInfo(thingtype); + } + } + + // This gets thing information by index + // Returns null when thing type info could not be found + public ThingTypeInfo GetThingInfoEx(int thingtype) + { + // Index in config? + if (thingtypes.ContainsKey(thingtype)) + { + // Return from config + return thingtypes[thingtype]; + } + else + { + // No such thing type known + return null; + } + } + + #endregion + + #region ================== Tools + + // This finds the first IWAD resource + // Returns false when not found + internal bool FindFirstIWAD(out DataLocation result) + { + // Go for all data containers + foreach (DataReader dr in containers) + { + // Container is a WAD file? + if (dr is WADReader) + { + // Check if it is an IWAD + WADReader wr = dr as WADReader; + if (wr.IsIWAD) + { + // Return location! + result = wr.Location; + return true; + } + } + } + + // No IWAD found + result = new DataLocation(); + return false; + } + + // This signals the background thread to update the + // used-in-map status on all textures and flats + public void UpdateUsedTextures() + { + lock (usedimages) + { + usedimages.Clear(); + + // Go through the map to find the used textures + foreach (Sidedef sd in General.Map.Map.Sidedefs) + { + // Add used textures to dictionary + if (sd.HighTexture.Length > 0) usedimages[sd.LongHighTexture] = 0; + if (sd.LowTexture.Length > 0) usedimages[sd.LongMiddleTexture] = 0; + if (sd.MiddleTexture.Length > 0) usedimages[sd.LongLowTexture] = 0; + } + + // Go through the map to find the used flats + foreach (Sector s in General.Map.Map.Sectors) + { + // Add used flats to dictionary + usedimages[s.LongFloorTexture] = 0; + usedimages[s.LongCeilTexture] = 0; + } + + // Notify the background thread that it needs to update the images + updatedusedtextures = true; + } + } + + // This returns the long name for a string + public long GetLongImageName(string name) + { + return Lump.MakeLongName(name); + } + + #endregion + } +} diff --git a/Source/Core/Data/DataReader.cs b/Source/Core/Data/DataReader.cs new file mode 100644 index 0000000..b8b15a4 --- /dev/null +++ b/Source/Core/Data/DataReader.cs @@ -0,0 +1,161 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Drawing; +using System.Drawing.Imaging; +using System.IO; +using CodeImp.DoomBuilder.Config; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Rendering; + +#endregion + +namespace CodeImp.DoomBuilder.Data +{ + internal abstract class DataReader + { + #region ================== Variables + + protected DataLocation location; + protected bool issuspended = false; + protected bool isdisposed = false; + protected ResourceTextureSet textureset; + + #endregion + + #region ================== Properties + + public DataLocation Location { get { return location; } } + public bool IsDisposed { get { return isdisposed; } } + public bool IsSuspended { get { return issuspended; } } + public ResourceTextureSet TextureSet { get { return textureset; } } + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + public DataReader(DataLocation dl) + { + // Keep information + location = dl; + textureset = new ResourceTextureSet(GetTitle(), dl); + } + + // Disposer + public virtual void Dispose() + { + // Not already disposed? + if (!isdisposed) + { + // Done + textureset = null; + isdisposed = true; + } + } + + #endregion + + #region ================== Management + + // This returns a short name + public abstract string GetTitle(); + + // This suspends use of this resource + public virtual void Suspend() + { + issuspended = true; + } + + // This resumes use of this resource + public virtual void Resume() + { + issuspended = false; + } + + #endregion + + #region ================== Palette + + // When implemented, this should find and load a PLAYPAL palette + public virtual Playpal LoadPalette() { return null; } + + // villsa + public virtual Playpal LoadThingPalette(string palname) { return null; } + + #endregion + + #region ================== Colormaps + + // When implemented, this loads the colormaps + public virtual ICollection LoadColormaps() { return null; } + + // When implemented, this returns the colormap lump + public virtual Stream GetColormapData(string pname) { return null; } + + #endregion + + #region ================== Textures + + // When implemented, this should read the patch names + public virtual PatchNames LoadPatchNames() { return null; } + + // When implemented, this returns the patch lump + public virtual Stream GetPatchData(string pname) { return null; } + + // When implemented, this returns the texture lump + public virtual Stream GetTextureData(string pname) { return null; } + + // When implemented, this loads the textures + public virtual ICollection LoadTextures(PatchNames pnames) { return null; } + + #endregion + + #region ================== Flats + + // When implemented, this loads the flats + public virtual ICollection LoadFlats() { return null; } + + // When implemented, this returns the flat lump + public virtual Stream GetFlatData(string pname) { return null; } + + #endregion + + #region ================== Sprites + + // When implemented, this returns the sprite lump + public virtual Stream GetSpriteData(string pname) { return null; } + + // When implemented, this checks if the given sprite lump exists + public virtual bool GetSpriteExists(string pname) { return false; } + + #endregion + + #region ================== Decorate + + // When implemented, this returns the decorate lump + public virtual List GetDecorateData(string pname) { return new List(); } + + #endregion + } +} diff --git a/Source/Core/Data/DirectoryReader.cs b/Source/Core/Data/DirectoryReader.cs new file mode 100644 index 0000000..15b841f --- /dev/null +++ b/Source/Core/Data/DirectoryReader.cs @@ -0,0 +1,334 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Drawing; +using System.Drawing.Imaging; +using System.IO; +using System.Windows.Forms; +using CodeImp.DoomBuilder.IO; + +#endregion + +namespace CodeImp.DoomBuilder.Data +{ + internal sealed class DirectoryReader : PK3StructuredReader + { + #region ================== Variables + + private DirectoryFilesList files; + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + public DirectoryReader(DataLocation dl) : base(dl) + { + General.WriteLogLine("Opening directory resource '" + location.location + "'"); + + // Initialize + files = new DirectoryFilesList(dl.location, true); + Initialize(); + + // We have no destructor + GC.SuppressFinalize(this); + } + + // Disposer + public override void Dispose() + { + // Not already disposed? + if (!isdisposed) + { + General.WriteLogLine("Closing directory resource '" + location.location + "'"); + + // Done + base.Dispose(); + } + } + + #endregion + + #region ================== Textures + + // This finds and returns a patch stream + public override Stream GetPatchData(string pname) + { + // Error when suspended + if (issuspended) throw new Exception("Data reader is suspended"); + + // Find in any of the wad files + // Note the backward order, because the last wad's images have priority + for (int i = wads.Count - 1; i >= 0; i--) + { + Stream data = wads[i].GetPatchData(pname); + if (data != null) return data; + } + + try + { + // Find in patches directory + string path = Path.Combine(PATCHES_DIR, Path.GetDirectoryName(pname)); + string filename = FindFirstFile(path, Path.GetFileName(pname), true); + if ((filename != null) && FileExists(filename)) + { + return LoadFile(filename); + } + } + catch (Exception e) + { + General.ErrorLogger.Add(ErrorType.Error, e.GetType().Name + " while loading patch '" + pname + "' from directory: " + e.Message); + } + + // Nothing found + return null; + } + + // This finds and returns a textue stream + public override Stream GetTextureData(string pname) + { + // Error when suspended + if (issuspended) throw new Exception("Data reader is suspended"); + + // Find in any of the wad files + // Note the backward order, because the last wad's images have priority + for (int i = wads.Count - 1; i >= 0; i--) + { + Stream data = wads[i].GetTextureData(pname); + if (data != null) return data; + } + + try + { + // Find in patches directory + string path = Path.Combine(TEXTURES_DIR, Path.GetDirectoryName(pname)); + string filename = FindFirstFile(path, Path.GetFileName(pname), true); + if ((filename != null) && FileExists(filename)) + { + return LoadFile(filename); + } + } + catch (Exception e) + { + General.ErrorLogger.Add(ErrorType.Error, e.GetType().Name + " while loading texture '" + pname + "' from directory: " + e.Message); + } + + // Nothing found + return null; + } + + // This finds and returns a colormap stream + public override Stream GetColormapData(string pname) + { + // Error when suspended + if (issuspended) throw new Exception("Data reader is suspended"); + + // Find in any of the wad files + // Note the backward order, because the last wad's images have priority + for (int i = wads.Count - 1; i >= 0; i--) + { + Stream data = wads[i].GetColormapData(pname); + if (data != null) return data; + } + + try + { + // Find in patches directory + string path = Path.Combine(COLORMAPS_DIR, Path.GetDirectoryName(pname)); + string filename = FindFirstFile(path, Path.GetFileName(pname), true); + if ((filename != null) && FileExists(filename)) + { + return LoadFile(filename); + } + } + catch (Exception e) + { + General.ErrorLogger.Add(ErrorType.Error, e.GetType().Name + " while loading colormap '" + pname + "' from directory: " + e.Message); + } + + // Nothing found + return null; + } + + #endregion + + #region ================== Sprites + + // This finds and returns a sprite stream + public override Stream GetSpriteData(string pname) + { + // Error when suspended + if (issuspended) throw new Exception("Data reader is suspended"); + + // Find in any of the wad files + for (int i = wads.Count - 1; i >= 0; i--) + { + Stream sprite = wads[i].GetSpriteData(pname); + if (sprite != null) return sprite; + } + + try + { + // Find in sprites directory + string path = Path.Combine(SPRITES_DIR, Path.GetDirectoryName(pname)); + string filename = FindFirstFile(path, Path.GetFileName(pname), true); + if ((filename != null) && FileExists(filename)) + { + return LoadFile(filename); + } + } + catch (Exception e) + { + General.ErrorLogger.Add(ErrorType.Error, e.GetType().Name + " while loading sprite '" + pname + "' from directory: " + e.Message); + } + + // Nothing found + return null; + } + + // This checks if the given sprite exists + public override bool GetSpriteExists(string pname) + { + // Error when suspended + if (issuspended) throw new Exception("Data reader is suspended"); + + // Find in any of the wad files + for (int i = wads.Count - 1; i >= 0; i--) + { + if (wads[i].GetSpriteExists(pname)) return true; + } + + // Find in sprites directory + try + { + string path = Path.Combine(SPRITES_DIR, Path.GetDirectoryName(pname)); + string filename = FindFirstFile(path, Path.GetFileName(pname), true); + if ((filename != null) && FileExists(filename)) + { + return true; + } + } + catch (Exception e) + { + General.ErrorLogger.Add(ErrorType.Error, e.GetType().Name + " while checking sprite '" + pname + "' existance in directory: " + e.Message); + } + + // Nothing found + return false; + } + + #endregion + + #region ================== Methods + + // Return a short name for this data location + public override string GetTitle() + { + return Path.GetFileName(location.location); + } + + // This creates an image + protected override ImageData CreateImage(string name, string filename, int imagetype) + { + switch (imagetype) + { + case ImageDataFormat.DOOMFLAT: + return new FileImage(name, Path.Combine(location.location, filename), true); + + case ImageDataFormat.DOOMPICTURE: + return new FileImage(name, Path.Combine(location.location, filename), false); + + case ImageDataFormat.DOOMCOLORMAP: + return new ColormapImage(name); + + default: + throw new ArgumentException("Invalid image format specified!"); + return null; + } + } + + // This returns true if the specified file exists + protected override bool FileExists(string filename) + { + return files.FileExists(filename); + } + + // This returns all files in a given directory + protected override string[] GetAllFiles(string path, bool subfolders) + { + return files.GetAllFiles(path, subfolders).ToArray(); + } + + // This returns all files in a given directory that have the given file title + protected override string[] GetAllFilesWithTitle(string path, string title, bool subfolders) + { + return files.GetAllFilesWithTitle(path, title, subfolders).ToArray(); + } + + // This returns all files in a given directory that match the given extension + protected override string[] GetFilesWithExt(string path, string extension, bool subfolders) + { + return files.GetAllFiles(path, extension, subfolders).ToArray(); + } + + // This finds the first file that has the specific name, regardless of file extension + protected override string FindFirstFile(string beginswith, bool subfolders) + { + return files.GetFirstFile(beginswith, subfolders); + } + + // This finds the first file that has the specific name, regardless of file extension + protected override string FindFirstFile(string path, string beginswith, bool subfolders) + { + return files.GetFirstFile(path, beginswith, subfolders); + } + + // This finds the first file that has the specific name + protected override string FindFirstFileWithExt(string path, string beginswith, bool subfolders) + { + string title = Path.GetFileNameWithoutExtension(beginswith); + string ext = Path.GetExtension(beginswith); + if (ext.Length > 1) ext = ext.Substring(1); else ext = ""; + return files.GetFirstFile(path, title, subfolders, ext); + } + + // This loads an entire file in memory and returns the stream + // NOTE: Callers are responsible for disposing the stream! + protected override MemoryStream LoadFile(string filename) + { + return new MemoryStream(File.ReadAllBytes(Path.Combine(location.location, filename))); + } + + // This creates a temp file for the speciied file and return the absolute path to the temp file + // NOTE: Callers are responsible for removing the temp file when done! + protected override string CreateTempFile(string filename) + { + // Just copy the file + string tempfile = General.MakeTempFilename(General.Map.TempPath, "wad"); + File.Copy(Path.Combine(location.location, filename), tempfile); + return tempfile; + } + + #endregion + } +} diff --git a/Source/Core/Data/FileImage.cs b/Source/Core/Data/FileImage.cs new file mode 100644 index 0000000..2867137 --- /dev/null +++ b/Source/Core/Data/FileImage.cs @@ -0,0 +1,137 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Drawing; +using System.Drawing.Imaging; +using System.IO; +using CodeImp.DoomBuilder.IO; + +#endregion + +namespace CodeImp.DoomBuilder.Data +{ + public sealed class FileImage : ImageData + { + #region ================== Variables + + private string filepathname; + private int probableformat; + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + public FileImage(string name, string filepathname, bool asflat) + { + // Initialize + this.filepathname = filepathname; + SetName(name); + + if (asflat) + { + probableformat = ImageDataFormat.DOOMFLAT; + this.scale.x = General.Map.Config.DefaultFlatScale; + this.scale.y = General.Map.Config.DefaultFlatScale; + } + else + { + probableformat = ImageDataFormat.DOOMPICTURE; + this.scale.x = General.Map.Config.DefaultTextureScale; + this.scale.y = General.Map.Config.DefaultTextureScale; + } + + // We have no destructor + GC.SuppressFinalize(this); + } + + // Constructor + public FileImage(string name, string filepathname, bool asflat, float scalex, float scaley) + { + // Initialize + this.filepathname = filepathname; + this.scale.x = scalex; + this.scale.y = scaley; + SetName(name); + + if (asflat) + probableformat = ImageDataFormat.DOOMFLAT; + else + probableformat = ImageDataFormat.DOOMPICTURE; + + // We have no destructor + GC.SuppressFinalize(this); + } + + #endregion + + #region ================== Methods + + // This loads the image + protected override void LocalLoadImage() + { + // Leave when already loaded + if (this.IsImageLoaded) return; + + lock (this) + { + // Load file data + if (bitmap != null) bitmap.Dispose(); bitmap = null; + MemoryStream filedata = new MemoryStream(File.ReadAllBytes(filepathname)); + + // Get a reader for the data + IImageReader reader = ImageDataFormat.GetImageReader(filedata, probableformat, General.Map.Data.Palette); + if (!(reader is UnknownImageReader)) + { + // Load the image + filedata.Seek(0, SeekOrigin.Begin); + try { bitmap = reader.ReadAsBitmap(filedata); } + catch (InvalidDataException) + { + // Data cannot be read! + bitmap = null; + } + } + + // Not loaded? + if (bitmap == null) + { + General.ErrorLogger.Add(ErrorType.Error, "Image file '" + filepathname + "' data format could not be read, while loading image '" + this.Name + "'. Is this a valid picture file at all?"); + loadfailed = true; + } + else + { + // Get width and height + width = bitmap.Size.Width; + height = bitmap.Size.Height; + } + + // Pass on to base + filedata.Dispose(); + base.LocalLoadImage(); + } + } + + #endregion + } +} diff --git a/Source/Core/Data/FlatImage.cs b/Source/Core/Data/FlatImage.cs new file mode 100644 index 0000000..263751f --- /dev/null +++ b/Source/Core/Data/FlatImage.cs @@ -0,0 +1,121 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Drawing; +using System.Drawing.Imaging; +using System.IO; +using CodeImp.DoomBuilder.IO; + +#endregion + +namespace CodeImp.DoomBuilder.Data +{ + internal sealed class FlatImage : ImageData + { + #region ================== Constructor / Disposer + + // Constructor + public FlatImage(string name) + { + // Initialize + SetName(name); + + // We have no destructor + GC.SuppressFinalize(this); + } + + #endregion + + #region ================== Methods + + // This loads the image + protected override void LocalLoadImage() + { + Stream lumpdata; + MemoryStream mem; + IImageReader reader; + byte[] membytes; + + // Leave when already loaded + if (this.IsImageLoaded) return; + + lock (this) + { + // Get the lump data stream + lumpdata = General.Map.Data.GetFlatData(Name); + if (lumpdata != null) + { + // Copy lump data to memory + lumpdata.Seek(0, SeekOrigin.Begin); + membytes = new byte[(int)lumpdata.Length]; + lumpdata.Read(membytes, 0, (int)lumpdata.Length); + mem = new MemoryStream(membytes); + mem.Seek(0, SeekOrigin.Begin); + + // Get a reader for the data + reader = ImageDataFormat.GetImageReader(mem, ImageDataFormat.DOOMFLAT, General.Map.Data.Palette); + if (reader is UnknownImageReader) + { + // Data is in an unknown format! + General.ErrorLogger.Add(ErrorType.Error, "Flat lump '" + Name + "' data format could not be read. Does this lump contain valid picture data at all?"); + bitmap = null; + } + else + { + // Read data as bitmap + mem.Seek(0, SeekOrigin.Begin); + if (bitmap != null) bitmap.Dispose(); + bitmap = reader.ReadAsBitmap(mem); + } + + // Done + mem.Dispose(); + + if (bitmap != null) + { + // Get width and height from image and set the scale + width = bitmap.Size.Width; + height = bitmap.Size.Height; + scale.x = General.Map.Config.DefaultFlatScale; + scale.y = General.Map.Config.DefaultFlatScale; + } + else + { + loadfailed = true; + } + } + else + { + // Missing a patch lump! + General.ErrorLogger.Add(ErrorType.Error, "Missing flat lump '" + Name + "'. Did you forget to include required resources?"); + loadfailed = true; + } + + // Pass on to base + base.LocalLoadImage(); + } + } + + #endregion + } +} diff --git a/Source/Core/Data/HighResImage.cs b/Source/Core/Data/HighResImage.cs new file mode 100644 index 0000000..a7b8a14 --- /dev/null +++ b/Source/Core/Data/HighResImage.cs @@ -0,0 +1,199 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Drawing; +using System.Drawing.Imaging; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.IO; +using System.IO; + +#endregion + +namespace CodeImp.DoomBuilder.Data +{ + internal sealed unsafe class HighResImage : ImageData + { + #region ================== Variables + + private List patches; + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + public HighResImage(string name, int width, int height, float scalex, float scaley, bool worldpanning) + { + // Initialize + this.width = width; + this.height = height; + this.scale.x = scalex; + this.scale.y = scaley; + this.worldpanning = worldpanning; + this.patches = new List(); + SetName(name); + + // We have no destructor + GC.SuppressFinalize(this); + } + + #endregion + + #region ================== Methods + + // This adds a patch to the texture + public void AddPatch(TexturePatch patch) + { + // Add it + patches.Add(patch); + } + + // This loads the image + protected override void LocalLoadImage() + { + IImageReader reader; + MemoryStream mem; + byte[] membytes; + Graphics g = null; + + // Checks + if (this.IsImageLoaded) return; + if ((width == 0) || (height == 0)) return; + + lock (this) + { + // Create texture bitmap + try + { + if (bitmap != null) bitmap.Dispose(); + bitmap = new Bitmap(width, height, PixelFormat.Format32bppArgb); + BitmapData bitmapdata = bitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb); + PixelColor* pixels = (PixelColor*)bitmapdata.Scan0.ToPointer(); + General.ZeroMemory(new IntPtr(pixels), width * height * sizeof(PixelColor)); + bitmap.UnlockBits(bitmapdata); + g = Graphics.FromImage(bitmap); + } + catch (Exception e) + { + // Unable to make bitmap + General.ErrorLogger.Add(ErrorType.Error, "Unable to load texture image '" + this.Name + "'. " + e.GetType().Name + ": " + e.Message); + loadfailed = true; + } + + if (!loadfailed) + { + // Go for all patches + foreach (TexturePatch p in patches) + { + // Get the patch data stream + Stream patchdata = General.Map.Data.GetPatchData(p.lumpname); + if (patchdata != null) + { + // Copy patch data to memory + patchdata.Seek(0, SeekOrigin.Begin); + membytes = new byte[(int)patchdata.Length]; + patchdata.Read(membytes, 0, (int)patchdata.Length); + mem = new MemoryStream(membytes); + mem.Seek(0, SeekOrigin.Begin); + + // Get a reader for the data + reader = ImageDataFormat.GetImageReader(mem, ImageDataFormat.DOOMPICTURE, General.Map.Data.Palette); + if (reader is UnknownImageReader) + { + // Data is in an unknown format! + General.ErrorLogger.Add(ErrorType.Error, "Patch lump '" + p.lumpname + "' data format could not be read, while loading texture '" + this.Name + "'"); + loadfailed = true; + } + else + { + // Get the patch + mem.Seek(0, SeekOrigin.Begin); + Bitmap patchbmp = null; + try { patchbmp = reader.ReadAsBitmap(mem); } + catch (InvalidDataException) + { + // Data cannot be read! + General.ErrorLogger.Add(ErrorType.Error, "Patch lump '" + p.lumpname + "' data format could not be read, while loading texture '" + this.Name + "'"); + loadfailed = true; + } + if (patchbmp != null) + { + // Adjust patch alpha + if (p.alpha < 1.0f) + { + BitmapData bmpdata = null; + try + { + bmpdata = patchbmp.LockBits(new Rectangle(0, 0, patchbmp.Size.Width, patchbmp.Size.Height), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb); + } + catch (Exception e) + { + General.ErrorLogger.Add(ErrorType.Error, "Cannot lock image '" + p.lumpname + "' for alpha adjustment. " + e.GetType().Name + ": " + e.Message); + } + + if (bmpdata != null) + { + PixelColor* pixels = (PixelColor*)(bmpdata.Scan0.ToPointer()); + int numpixels = bmpdata.Width * bmpdata.Height; + for (PixelColor* cp = pixels + numpixels - 1; cp >= pixels; cp--) + { + cp->a = (byte)((((float)cp->a * PixelColor.BYTE_TO_FLOAT) * p.alpha) * 255.0f); + } + patchbmp.UnlockBits(bmpdata); + } + } + + // Draw the patch on the texture image + Rectangle tgtrect = new Rectangle(p.x, p.y, patchbmp.Size.Width, patchbmp.Size.Height); + g.DrawImageUnscaledAndClipped(patchbmp, tgtrect); + patchbmp.Dispose(); + } + } + + // Done + mem.Dispose(); + } + else + { + // Missing a patch lump! + General.ErrorLogger.Add(ErrorType.Error, "Missing patch lump '" + p.lumpname + "' while loading texture '" + this.Name + "'"); + loadfailed = true; + } + } + } + + // Dispose bitmap if load failed + if (loadfailed && (bitmap != null)) + { + bitmap.Dispose(); + bitmap = null; + } + + // Pass on to base + base.LocalLoadImage(); + } + } + + #endregion + } +} diff --git a/Source/Core/Data/ImageData.cs b/Source/Core/Data/ImageData.cs new file mode 100644 index 0000000..321de80 --- /dev/null +++ b/Source/Core/Data/ImageData.cs @@ -0,0 +1,437 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Drawing.Drawing2D; +using System.Globalization; +using System.Text; +using System.Drawing; +using CodeImp.DoomBuilder.Geometry; +using SlimDX.Direct3D9; +using System.Drawing.Imaging; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.IO; +using System.IO; +using System.Windows.Forms; +using System.Runtime.InteropServices; +using CodeImp.DoomBuilder.Windows; +using CodeImp.DoomBuilder.Config; // villsa + +#endregion + +namespace CodeImp.DoomBuilder.Data +{ + public abstract unsafe class ImageData + { + #region ================== Constants + + #endregion + + #region ================== Variables + + // Properties + private string name; + private long longname; + protected int width; + protected int height; + protected Vector2D scale; + protected bool worldpanning; + protected bool usecolorcorrection; + private int palindex; // villsa + + // Loading + private volatile ImageLoadState previewstate; + private volatile ImageLoadState imagestate; + private volatile int previewindex; + protected volatile bool loadfailed; + private volatile bool allowunload; + + // References + private volatile bool usedinmap; + private volatile int references; + + // GDI bitmap + protected Bitmap bitmap; + + // Direct3D texture + private int mipmaplevels = 0; // 0 = all mipmaps + private Texture texture; + + // Disposing + protected bool isdisposed = false; + + #endregion + + #region ================== Properties + + public string Name { get { return name; } } + public long LongName { get { return longname; } } + public bool UseColorCorrection { get { return usecolorcorrection; } set { usecolorcorrection = value; } } + public Texture Texture { get { lock (this) { return texture; } } } + public bool IsPreviewLoaded { get { return (previewstate == ImageLoadState.Ready); } } + public bool IsImageLoaded { get { return (imagestate == ImageLoadState.Ready); } } + public bool LoadFailed { get { return loadfailed; } } + public bool IsDisposed { get { return isdisposed; } } + public bool AllowUnload { get { return allowunload; } set { allowunload = value; } } + public ImageLoadState ImageState { get { return imagestate; } internal set { imagestate = value; } } + public ImageLoadState PreviewState { get { return previewstate; } internal set { previewstate = value; } } + public bool IsReferenced { get { return (references > 0) || usedinmap; } } + public bool UsedInMap { get { return usedinmap; } } + public int MipMapLevels { get { return mipmaplevels; } set { mipmaplevels = value; } } + public int Width { get { return width; } } + public int Height { get { return height; } } + internal int PreviewIndex { get { return previewindex; } set { previewindex = value; } } + public float ScaledWidth { get { return width * scale.x; } } + public float ScaledHeight { get { return height * scale.y; } } + public Vector2D Scale { get { return scale; } } + public bool WorldPanning { get { return worldpanning; } } + public int PalIndex { get { return palindex; } set { palindex = value; } } // villsa + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + public ImageData() + { + // Defaults + usecolorcorrection = true; + allowunload = true; + palindex = 0; // villsa + } + + // Destructor + ~ImageData() + { + this.Dispose(); + } + + // Disposer + public virtual void Dispose() + { + // Not already disposed? + if (!isdisposed) + { + lock (this) + { + // Clean up + if (bitmap != null) bitmap.Dispose(); + if (texture != null) texture.Dispose(); + bitmap = null; + texture = null; + + // Done + usedinmap = false; + imagestate = ImageLoadState.None; + previewstate = ImageLoadState.None; + isdisposed = true; + } + } + } + + #endregion + + #region ================== Management + + // This sets the status of the texture usage in the map + internal void SetUsedInMap(bool used) + { + if (used != usedinmap) + { + usedinmap = used; + General.Map.Data.ProcessImage(this); + } + } + + // This adds a reference + public void AddReference() + { + references++; + if (references == 1) General.Map.Data.ProcessImage(this); + } + + // This removes a reference + public void RemoveReference() + { + references--; + if (references < 0) General.Fail("FAIL! (references < 0) Somewhere this image is dereferenced more than it was referenced."); + if (references == 0) General.Map.Data.ProcessImage(this); + } + + // This sets the name + protected void SetName(string name) + { + this.name = name; + this.longname = Lump.MakeLongName(name); + } + + // This unloads the image + public virtual void UnloadImage() + { + lock (this) + { + if (bitmap != null) bitmap.Dispose(); + bitmap = null; + imagestate = ImageLoadState.None; + } + } + + // This returns the bitmap image + public Bitmap GetBitmap() + { + lock (this) + { + // Image loaded successfully? + if (!loadfailed && (imagestate == ImageLoadState.Ready) && (bitmap != null)) + { + return bitmap; + } + // Image loading failed? + else if (loadfailed) + { + return Properties.Resources.Failed; + } + else + { + return Properties.Resources.Hourglass; + } + } + } + + // This loads the image + public void LoadImage() + { + // Do the loading + LocalLoadImage(); + + // Notify the main thread about the change so that sectors can update their buffers + IntPtr strptr = Marshal.StringToCoTaskMemAuto(this.name); + General.SendMessage(General.MainWindow.Handle, (int)MainForm.ThreadMessages.ImageDataLoaded, strptr.ToInt32(), 0); + } + + // This requests loading the image + protected virtual void LocalLoadImage() + { + BitmapData bmpdata = null; + + lock (this) + { + // Bitmap loaded successfully? + if (bitmap != null) + { + // Bitmap has incorrect format? + if (bitmap.PixelFormat != PixelFormat.Format32bppArgb) + { + // villsa + if (General.Map.FormatInterface.InDoom64Mode) + { + ColorPalette ncp = bitmap.Palette; + + foreach (TextureIndexInfo tp in General.Map.Config.ThingPalettes) + { + // sprite contains alternate palette? + if (General.Map.Data.ThingPalette.ContainsKey(tp.Title) + && tp.Index == palindex) + { + Playpal pal = General.Map.Data.ThingPalette[tp.Title]; + for (int i = 0; i < 255; i++) + { + // copy alternate palette over + ncp.Entries[i] = pal[i].ToColor(); + } + } + } + } + + //General.ErrorLogger.Add(ErrorType.Warning, "Image '" + name + "' does not have A8R8G8B8 pixel format. Conversion was needed."); + Bitmap oldbitmap = bitmap; + try + { + // Convert to desired pixel format + bitmap = new Bitmap(oldbitmap.Size.Width, oldbitmap.Size.Height, PixelFormat.Format32bppArgb); + Graphics g = Graphics.FromImage(bitmap); + g.PageUnit = GraphicsUnit.Pixel; + g.CompositingQuality = CompositingQuality.HighQuality; + g.InterpolationMode = InterpolationMode.NearestNeighbor; + g.SmoothingMode = SmoothingMode.None; + g.PixelOffsetMode = PixelOffsetMode.None; + g.Clear(Color.Transparent); + g.DrawImage(oldbitmap, 0, 0, oldbitmap.Size.Width, oldbitmap.Size.Height); + g.Dispose(); + oldbitmap.Dispose(); + } + catch (Exception e) + { + bitmap = oldbitmap; + General.ErrorLogger.Add(ErrorType.Warning, "Cannot lock image '" + name + "' for pixel format conversion. The image may not be displayed correctly.\n" + e.GetType().Name + ": " + e.Message); + } + } + + // This applies brightness correction on the image + if (usecolorcorrection) + { + try + { + // Try locking the bitmap + bmpdata = bitmap.LockBits(new Rectangle(0, 0, bitmap.Size.Width, bitmap.Size.Height), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb); + } + catch (Exception e) + { + General.ErrorLogger.Add(ErrorType.Warning, "Cannot lock image '" + name + "' for color correction. The image may not be displayed correctly.\n" + e.GetType().Name + ": " + e.Message); + } + + // Bitmap locked? + if (bmpdata != null) + { + // Apply color correction + PixelColor* pixels = (PixelColor*)(bmpdata.Scan0.ToPointer()); + General.Colors.ApplColorCorrection(pixels, bmpdata.Width * bmpdata.Height); + bitmap.UnlockBits(bmpdata); + } + } + } + else + { + // Loading failed + // We still mark the image as ready so that it will + // not try loading again until Reload Resources is used + loadfailed = true; + bitmap = new Bitmap(Properties.Resources.Failed); + } + + if (bitmap != null) + { + width = bitmap.Size.Width; + height = bitmap.Size.Height; + + // Do we still have to set a scale? + if ((scale.x == 0.0f) && (scale.y == 0.0f)) + { + if ((General.Map != null) && (General.Map.Config != null)) + { + scale.x = General.Map.Config.DefaultTextureScale; + scale.y = General.Map.Config.DefaultTextureScale; + } + else + { + scale.x = 1.0f; + scale.y = 1.0f; + } + } + } + + // Image is ready + imagestate = ImageLoadState.Ready; + } + } + + // This creates the Direct3D texture + public virtual void CreateTexture() + { + MemoryStream memstream; + + lock (this) + { + // Only do this when texture is not created yet + if (((texture == null) || (texture.Disposed)) && this.IsImageLoaded && !loadfailed) + { + Image img = bitmap; + if (loadfailed) img = Properties.Resources.Failed; + + // Write to memory stream and read from memory + memstream = new MemoryStream((img.Size.Width * img.Size.Height * 4) + 4096); + img.Save(memstream, ImageFormat.Bmp); + memstream.Seek(0, SeekOrigin.Begin); + texture = Texture.FromStream(General.Map.Graphics.Device, memstream, (int)memstream.Length, + img.Size.Width, img.Size.Height, mipmaplevels, Usage.None, Format.Unknown, + Pool.Managed, General.Map.Graphics.PostFilter, General.Map.Graphics.MipGenerateFilter, 0); + memstream.Dispose(); + } + } + } + + // This destroys the Direct3D texture + public void ReleaseTexture() + { + lock (this) + { + // Trash it + if (texture != null) texture.Dispose(); + texture = null; + } + } + + // This draws a preview + public virtual void DrawPreview(Graphics target, Point targetpos) + { + lock (this) + { + // Preview ready? + if (!loadfailed && (previewstate == ImageLoadState.Ready)) + { + // Draw preview + General.Map.Data.Previews.DrawPreview(previewindex, target, targetpos); + } + // Loading failed? + else if (loadfailed) + { + // Draw error bitmap + targetpos = new Point(targetpos.X + ((General.Map.Data.Previews.MaxImageWidth - Properties.Resources.Hourglass.Width) >> 1), + targetpos.Y + ((General.Map.Data.Previews.MaxImageHeight - Properties.Resources.Hourglass.Height) >> 1)); + target.DrawImageUnscaled(Properties.Resources.Failed, targetpos); + } + else + { + // Draw loading bitmap + targetpos = new Point(targetpos.X + ((General.Map.Data.Previews.MaxImageWidth - Properties.Resources.Hourglass.Width) >> 1), + targetpos.Y + ((General.Map.Data.Previews.MaxImageHeight - Properties.Resources.Hourglass.Height) >> 1)); + target.DrawImageUnscaled(Properties.Resources.Hourglass, targetpos); + } + } + } + + // This returns a preview image + public virtual Image GetPreview() + { + lock (this) + { + // Preview ready? + if (previewstate == ImageLoadState.Ready) + { + // Make a copy + return General.Map.Data.Previews.GetPreviewCopy(previewindex); + } + // Loading failed? + else if (loadfailed) + { + // Return error bitmap + return Properties.Resources.Failed; + } + else + { + // Return loading bitmap + return Properties.Resources.Hourglass; + } + } + } + + #endregion + } +} diff --git a/Source/Core/Data/ImageDataFormat.cs b/Source/Core/Data/ImageDataFormat.cs new file mode 100644 index 0000000..05fc29f --- /dev/null +++ b/Source/Core/Data/ImageDataFormat.cs @@ -0,0 +1,124 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.IO; +using System.Drawing.Imaging; +using CodeImp.DoomBuilder.IO; + +#endregion + +namespace CodeImp.DoomBuilder.Data +{ + internal static class ImageDataFormat + { + // Input guess formats + public const int UNKNOWN = 0; // No clue. + public const int DOOMPICTURE = 1; // Could be Doom Picture format (column list rendered data) + public const int DOOMFLAT = 2; // Could be Doom Flat format (raw 8-bit pixel data) + public const int DOOMCOLORMAP = 3; // Could be Doom Colormap format (raw 8-bit pixel palette mapping) + + // File format signatures + private static readonly int[] PNG_SIGNATURE = new int[] { 137, 80, 78, 71, 13, 10, 26, 10 }; + private static readonly int[] GIF_SIGNATURE = new int[] { 71, 73, 70 }; + private static readonly int[] BMP_SIGNATURE = new int[] { 66, 77 }; + + // This check image data and returns the appropriate image reader + public static IImageReader GetImageReader(Stream data, int guessformat, Playpal palette) + { + BinaryReader bindata = new BinaryReader(data); + DoomPictureReader picreader; + DoomFlatReader flatreader; + DoomColormapReader colormapreader; + + // First check the formats that provide the means to 'ensure' that + // it actually is that format. Then guess the Doom image format. + + // Data long enough to check for signatures? + if (data.Length > 10) + { + // Check for PNG signature + data.Seek(0, SeekOrigin.Begin); + if (CheckSignature(data, PNG_SIGNATURE)) return new FileImageReader(); + + // Check for GIF signature + data.Seek(0, SeekOrigin.Begin); + if (CheckSignature(data, GIF_SIGNATURE)) return new FileImageReader(); + + // Check for BMP signature + data.Seek(0, SeekOrigin.Begin); + if (CheckSignature(data, BMP_SIGNATURE)) + { + // Check if data size matches the size specified in the data + if (bindata.ReadUInt32() <= data.Length) return new FileImageReader(); + } + } + + // Could it be a doom picture? + if (guessformat == DOOMPICTURE) + { + // Check if data is valid for a doom picture + data.Seek(0, SeekOrigin.Begin); + picreader = new DoomPictureReader(palette); + if (picreader.Validate(data)) return picreader; + } + // Could it be a doom flat? + else if (guessformat == DOOMFLAT) + { + // Check if data is valid for a doom flat + data.Seek(0, SeekOrigin.Begin); + flatreader = new DoomFlatReader(palette); + if (flatreader.Validate(data)) return flatreader; + } + // Could it be a doom colormap? + else if (guessformat == DOOMCOLORMAP) + { + // Check if data is valid for a doom colormap + data.Seek(0, SeekOrigin.Begin); + colormapreader = new DoomColormapReader(palette); + if (colormapreader.Validate(data)) return colormapreader; + } + + // Format not supported + return new UnknownImageReader(); + } + + // This checks a signature as byte array + // NOTE: Expects the stream position to be at the start of the + // signature, and expects the stream to be long enough. + private static bool CheckSignature(Stream data, int[] sig) + { + int b; + + // Go for all bytes + for (int i = 0; i < sig.Length; i++) + { + // When byte doesnt match the signature, leave + b = data.ReadByte(); + if (b != sig[i]) return false; + } + + // Signature matches + return true; + } + } +} diff --git a/Source/Core/Data/ImageLoadState.cs b/Source/Core/Data/ImageLoadState.cs new file mode 100644 index 0000000..2f2be71 --- /dev/null +++ b/Source/Core/Data/ImageLoadState.cs @@ -0,0 +1,43 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Drawing; +using SlimDX.Direct3D9; +using System.Drawing.Imaging; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.IO; +using System.IO; +using System.Windows.Forms; + +#endregion + +namespace CodeImp.DoomBuilder.Data +{ + public enum ImageLoadState : int + { + None, + Loading, + Ready, + Unloading + } +} diff --git a/Source/Core/Data/PK3FileImage.cs b/Source/Core/Data/PK3FileImage.cs new file mode 100644 index 0000000..1112eb7 --- /dev/null +++ b/Source/Core/Data/PK3FileImage.cs @@ -0,0 +1,121 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Drawing; +using System.Drawing.Imaging; +using System.IO; +using CodeImp.DoomBuilder.IO; + +#endregion + +namespace CodeImp.DoomBuilder.Data +{ + public sealed class PK3FileImage : ImageData + { + #region ================== Variables + + private PK3Reader datareader; + private string filepathname; + private int probableformat; + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + internal PK3FileImage(PK3Reader datareader, string name, string filepathname, bool asflat) + { + // Initialize + this.datareader = datareader; + this.filepathname = filepathname; + SetName(name); + + if (asflat) + { + probableformat = ImageDataFormat.DOOMFLAT; + this.scale.x = General.Map.Config.DefaultFlatScale; + this.scale.y = General.Map.Config.DefaultFlatScale; + } + else + { + probableformat = ImageDataFormat.DOOMPICTURE; + this.scale.x = General.Map.Config.DefaultTextureScale; + this.scale.y = General.Map.Config.DefaultTextureScale; + } + + // We have no destructor + GC.SuppressFinalize(this); + } + + #endregion + + #region ================== Methods + + // This loads the image + protected override void LocalLoadImage() + { + // Leave when already loaded + if (this.IsImageLoaded) return; + + lock (this) + { + // Load file data + if (bitmap != null) bitmap.Dispose(); bitmap = null; + MemoryStream filedata = datareader.ExtractFile(filepathname); + + // Get a reader for the data + IImageReader reader = ImageDataFormat.GetImageReader(filedata, probableformat, General.Map.Data.Palette); + if (!(reader is UnknownImageReader)) + { + // Load the image + filedata.Seek(0, SeekOrigin.Begin); + try { bitmap = reader.ReadAsBitmap(filedata); } + catch (InvalidDataException) + { + // Data cannot be read! + bitmap = null; + } + } + + // Not loaded? + if (bitmap == null) + { + General.ErrorLogger.Add(ErrorType.Error, "Image file '" + filepathname + "' data format could not be read, while loading texture '" + this.Name + "'"); + loadfailed = true; + } + else + { + // Get width and height from image + width = bitmap.Size.Width; + height = bitmap.Size.Height; + } + + // Pass on to base + filedata.Dispose(); + base.LocalLoadImage(); + } + } + + #endregion + } +} diff --git a/Source/Core/Data/PK3Reader.cs b/Source/Core/Data/PK3Reader.cs new file mode 100644 index 0000000..cb15cd1 --- /dev/null +++ b/Source/Core/Data/PK3Reader.cs @@ -0,0 +1,383 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Drawing; +using System.Drawing.Imaging; +using System.IO; +using CodeImp.DoomBuilder.IO; +using ICSharpCode.SharpZipLib.Zip; + +#endregion + +namespace CodeImp.DoomBuilder.Data +{ + internal sealed class PK3Reader : PK3StructuredReader + { + #region ================== Variables + + private DirectoryFilesList files; + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + public PK3Reader(DataLocation dl) : base(dl) + { + General.WriteLogLine("Opening PK3 resource '" + location.location + "'"); + + // Open the zip file + ZipInputStream zipstream = OpenPK3File(); + + // Make list of all files + List fileentries = new List(); + ZipEntry entry = zipstream.GetNextEntry(); + while (entry != null) + { + if (entry.IsFile) fileentries.Add(new DirectoryFileEntry(entry.Name)); + + // Next + entry = zipstream.GetNextEntry(); + } + + // Make files list + files = new DirectoryFilesList(fileentries); + + // Done with the zip file + zipstream.Close(); + zipstream.Dispose(); + + // Initialize without path (because we use paths relative to the PK3 file) + Initialize(); + + // We have no destructor + GC.SuppressFinalize(this); + } + + // Disposer + public override void Dispose() + { + // Not already disposed? + if (!isdisposed) + { + General.WriteLogLine("Closing PK3 resource '" + location.location + "'"); + + // Done + base.Dispose(); + } + } + + #endregion + + #region ================== Management + + // This opens the zip file for reading + private ZipInputStream OpenPK3File() + { + FileStream filestream = File.Open(location.location, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); + filestream.Seek(0, SeekOrigin.Begin); + return new ZipInputStream(filestream); + } + + #endregion + + #region ================== Textures + + // This finds and returns a patch stream + public override Stream GetPatchData(string pname) + { + // Error when suspended + if (issuspended) throw new Exception("Data reader is suspended"); + + // Find in any of the wad files + // Note the backward order, because the last wad's images have priority + for (int i = wads.Count - 1; i >= 0; i--) + { + Stream data = wads[i].GetPatchData(pname); + if (data != null) return data; + } + + // Find in patches directory + string filename = FindFirstFile(PATCHES_DIR, pname, true); + if ((filename != null) && FileExists(filename)) + { + return LoadFile(filename); + } + + // Nothing found + return null; + } + + // This finds and returns a textue stream + public override Stream GetTextureData(string pname) + { + // Error when suspended + if (issuspended) throw new Exception("Data reader is suspended"); + + // Find in any of the wad files + // Note the backward order, because the last wad's images have priority + for (int i = wads.Count - 1; i >= 0; i--) + { + Stream data = wads[i].GetTextureData(pname); + if (data != null) return data; + } + + // Find in patches directory + string filename = FindFirstFile(TEXTURES_DIR, pname, true); + if ((filename != null) && FileExists(filename)) + { + return LoadFile(filename); + } + + // Nothing found + return null; + } + + // This finds and returns a colormap stream + public override Stream GetColormapData(string pname) + { + // Error when suspended + if (issuspended) throw new Exception("Data reader is suspended"); + + // Find in any of the wad files + // Note the backward order, because the last wad's images have priority + for (int i = wads.Count - 1; i >= 0; i--) + { + Stream data = wads[i].GetColormapData(pname); + if (data != null) return data; + } + + // Find in patches directory + string filename = FindFirstFile(COLORMAPS_DIR, pname, true); + if ((filename != null) && FileExists(filename)) + { + return LoadFile(filename); + } + + // Nothing found + return null; + } + + #endregion + + #region ================== Sprites + + // This finds and returns a sprite stream + public override Stream GetSpriteData(string pname) + { + string pfilename = pname.Replace('\\', '^'); + + // Error when suspended + if (issuspended) throw new Exception("Data reader is suspended"); + + // Find in any of the wad files + for (int i = wads.Count - 1; i >= 0; i--) + { + Stream sprite = wads[i].GetSpriteData(pname); + if (sprite != null) return sprite; + } + + // Find in sprites directory + string filename = FindFirstFile(SPRITES_DIR, pfilename, true); + if ((filename != null) && FileExists(filename)) + { + return LoadFile(filename); + } + + // Nothing found + return null; + } + + // This checks if the given sprite exists + public override bool GetSpriteExists(string pname) + { + string pfilename = pname.Replace('\\', '^'); + + // Error when suspended + if (issuspended) throw new Exception("Data reader is suspended"); + + // Find in any of the wad files + for (int i = wads.Count - 1; i >= 0; i--) + { + if (wads[i].GetSpriteExists(pname)) return true; + } + + // Find in sprites directory + string filename = FindFirstFile(SPRITES_DIR, pfilename, true); + if ((filename != null) && FileExists(filename)) + { + return true; + } + + // Nothing found + return false; + } + + #endregion + + #region ================== Methods + + // Return a short name for this data location + public override string GetTitle() + { + return Path.GetFileName(location.location); + } + + // This creates an image + protected override ImageData CreateImage(string name, string filename, int imagetype) + { + switch (imagetype) + { + case ImageDataFormat.DOOMFLAT: + return new PK3FileImage(this, name, filename, true); + + case ImageDataFormat.DOOMPICTURE: + return new PK3FileImage(this, name, filename, false); + + case ImageDataFormat.DOOMCOLORMAP: + return new ColormapImage(name); + + default: + throw new ArgumentException("Invalid image format specified!"); + return null; + } + } + + // This returns true if the specified file exists + protected override bool FileExists(string filename) + { + return files.FileExists(filename); + } + + // This returns all files in a given directory + protected override string[] GetAllFiles(string path, bool subfolders) + { + return files.GetAllFiles(path, subfolders).ToArray(); + } + + // This returns all files in a given directory that have the given title + protected override string[] GetAllFilesWithTitle(string path, string title, bool subfolders) + { + return files.GetAllFilesWithTitle(path, title, subfolders).ToArray(); + } + + // This returns all files in a given directory that match the given extension + protected override string[] GetFilesWithExt(string path, string extension, bool subfolders) + { + return files.GetAllFiles(path, extension, subfolders).ToArray(); + } + + // This finds the first file that has the specific name, regardless of file extension + protected override string FindFirstFile(string beginswith, bool subfolders) + { + return files.GetFirstFile(beginswith, subfolders); + } + + // This finds the first file that has the specific name, regardless of file extension + protected override string FindFirstFile(string path, string beginswith, bool subfolders) + { + return files.GetFirstFile(path, beginswith, subfolders); + } + + // This finds the first file that has the specific name + protected override string FindFirstFileWithExt(string path, string beginswith, bool subfolders) + { + string title = Path.GetFileNameWithoutExtension(beginswith); + string ext = Path.GetExtension(beginswith); + if (ext.Length > 1) ext = ext.Substring(1); else ext = ""; + return files.GetFirstFile(path, title, subfolders, ext); + } + + // This loads an entire file in memory and returns the stream + // NOTE: Callers are responsible for disposing the stream! + protected override MemoryStream LoadFile(string filename) + { + MemoryStream filedata = null; + byte[] copybuffer = new byte[4096]; + + // Open the zip file + ZipInputStream zipstream = OpenPK3File(); + + ZipEntry entry = zipstream.GetNextEntry(); + while (entry != null) + { + if (entry.IsFile) + { + string entryname = entry.Name.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar); + + // Is this the entry we are looking for? + if (string.Compare(entryname, filename, true) == 0) + { + int expectedsize = (int)entry.Size; + if (expectedsize < 1) expectedsize = 1024; + filedata = new MemoryStream(expectedsize); + int readsize = zipstream.Read(copybuffer, 0, copybuffer.Length); + while (readsize > 0) + { + filedata.Write(copybuffer, 0, readsize); + readsize = zipstream.Read(copybuffer, 0, copybuffer.Length); + } + break; + } + } + + // Next + entry = zipstream.GetNextEntry(); + } + + // Done with the zip file + zipstream.Close(); + zipstream.Dispose(); + + // Nothing found? + if (filedata == null) + { + throw new FileNotFoundException("Cannot find the file " + filename + " in PK3 file " + location.location + "."); + } + else + { + return filedata; + } + } + + // This creates a temp file for the speciied file and return the absolute path to the temp file + // NOTE: Callers are responsible for removing the temp file when done! + protected override string CreateTempFile(string filename) + { + // Just copy the file + string tempfile = General.MakeTempFilename(General.Map.TempPath, "wad"); + MemoryStream filedata = LoadFile(filename); + File.WriteAllBytes(tempfile, filedata.ToArray()); + filedata.Dispose(); + return tempfile; + } + + // Public version to load a file + internal MemoryStream ExtractFile(string filename) + { + return LoadFile(filename); + } + + #endregion + } +} diff --git a/Source/Core/Data/PK3StructuredReader.cs b/Source/Core/Data/PK3StructuredReader.cs new file mode 100644 index 0000000..e75d134 --- /dev/null +++ b/Source/Core/Data/PK3StructuredReader.cs @@ -0,0 +1,467 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Drawing; +using System.Drawing.Imaging; +using System.IO; +using CodeImp.DoomBuilder.IO; + +#endregion + +namespace CodeImp.DoomBuilder.Data +{ + internal abstract class PK3StructuredReader : DataReader + { + #region ================== Constants + + protected const string PATCHES_DIR = "patches"; + protected const string TEXTURES_DIR = "textures"; + protected const string FLATS_DIR = "flats"; + protected const string HIRES_DIR = "hires"; + protected const string SPRITES_DIR = "sprites"; + protected const string COLORMAPS_DIR = "colormaps"; + + #endregion + + #region ================== Variables + + // Source + protected bool roottextures; + protected bool rootflats; + + // WAD files that must be loaded as well + protected List wads; + + #endregion + + #region ================== Properties + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + public PK3StructuredReader(DataLocation dl) : base(dl) + { + // Initialize + this.roottextures = dl.option1; + this.rootflats = dl.option2; + } + + // Call this to initialize this class + protected virtual void Initialize() + { + // Load all WAD files in the root as WAD resources + string[] wadfiles = GetFilesWithExt("", "wad", false); + wads = new List(wadfiles.Length); + foreach (string w in wadfiles) + { + string tempfile = CreateTempFile(w); + DataLocation wdl = new DataLocation(DataLocation.RESOURCE_WAD, tempfile, false, false, true); + wads.Add(new WADReader(wdl)); + } + } + + // Disposer + public override void Dispose() + { + // Not already disposed? + if (!isdisposed) + { + // Clean up + foreach (WADReader wr in wads) wr.Dispose(); + + // Remove temp files + foreach (WADReader wr in wads) + { + try { File.Delete(wr.Location.location); } + catch (Exception) { } + } + + // Done + base.Dispose(); + } + } + + #endregion + + #region ================== Management + + // This suspends use of this resource + public override void Suspend() + { + foreach (WADReader wr in wads) wr.Suspend(); + base.Suspend(); + } + + // This resumes use of this resource + public override void Resume() + { + foreach (WADReader wr in wads) wr.Resume(); + base.Resume(); + } + + #endregion + + #region ================== Palette + + // This loads the PLAYPAL palette + public override Playpal LoadPalette() + { + // Error when suspended + if (issuspended) throw new Exception("Data reader is suspended"); + + // Palette from wad(s) + Playpal palette = null; + foreach (WADReader wr in wads) + { + Playpal wadpalette = wr.LoadPalette(); + if (wadpalette != null) return wadpalette; + } + + // Find in root directory + string foundfile = FindFirstFile("PLAYPAL", false); + if ((foundfile != null) && FileExists(foundfile)) + { + MemoryStream stream = LoadFile(foundfile); + palette = new Playpal(stream); + stream.Dispose(); + } + + // Done + return palette; + } + + #endregion + + #region ================== Textures + + // This loads the textures + public override ICollection LoadTextures(PatchNames pnames) + { + Dictionary images = new Dictionary(); + ICollection collection; + List imgset = new List(); + + // Error when suspended + if (issuspended) throw new Exception("Data reader is suspended"); + + // Load from wad files (NOTE: backward order, because the last wad's images have priority) + for (int i = wads.Count - 1; i >= 0; i--) + { + collection = wads[i].LoadTextures(pnames); + AddImagesToList(images, collection); + } + + // Should we load the images in this directory as textures? + if (roottextures) + { + collection = LoadDirectoryImages("", ImageDataFormat.DOOMPICTURE, false); + AddImagesToList(images, collection); + } + + // Add images from texture directory + collection = LoadDirectoryImages(TEXTURES_DIR, ImageDataFormat.DOOMPICTURE, true); + AddImagesToList(images, collection); + + // Load TEXTURE1 lump file + imgset.Clear(); + string texture1file = FindFirstFile("TEXTURE1", false); + if ((texture1file != null) && FileExists(texture1file)) + { + MemoryStream filedata = LoadFile(texture1file); + WADReader.LoadTextureSet("TEXTURE1", filedata, ref imgset, pnames); + filedata.Dispose(); + } + + // Load TEXTURE2 lump file + string texture2file = FindFirstFile("TEXTURE2", false); + if ((texture2file != null) && FileExists(texture2file)) + { + MemoryStream filedata = LoadFile(texture2file); + WADReader.LoadTextureSet("TEXTURE2", filedata, ref imgset, pnames); + filedata.Dispose(); + } + + // Add images from TEXTURE1 and TEXTURE2 lump files + AddImagesToList(images, imgset); + + // Load TEXTURES lump file + imgset.Clear(); + string[] alltexturefiles = GetAllFilesWithTitle("", "TEXTURES", false); + foreach (string texturesfile in alltexturefiles) + { + MemoryStream filedata = LoadFile(texturesfile); + WADReader.LoadHighresTextures(filedata, texturesfile, ref imgset, images, null); + filedata.Dispose(); + } + + // Add images from TEXTURES lump file + AddImagesToList(images, imgset); + + // Add images to the container-specific texture set + foreach (ImageData img in images.Values) + textureset.AddTexture(img); + + return new List(images.Values); + } + + // This returns the patch names from the PNAMES lump + // A directory resource does not support this lump, but the wads in the directory may contain this lump + public override PatchNames LoadPatchNames() + { + PatchNames pnames; + + // Error when suspended + if (issuspended) throw new Exception("Data reader is suspended"); + + // Load from wad files + // Note the backward order, because the last wad's images have priority + for (int i = wads.Count - 1; i >= 0; i--) + { + pnames = wads[i].LoadPatchNames(); + if (pnames != null) return pnames; + } + + // If none of the wads provides patch names, let's see if we can + string pnamesfile = FindFirstFile("PNAMES", false); + if ((pnamesfile != null) && FileExists(pnamesfile)) + { + MemoryStream pnamesdata = LoadFile(pnamesfile); + pnames = new PatchNames(pnamesdata); + pnamesdata.Dispose(); + return pnames; + } + + return null; + } + + #endregion + + #region ================== Flats + + // This loads the textures + public override ICollection LoadFlats() + { + Dictionary images = new Dictionary(); + ICollection collection; + + // Error when suspended + if (issuspended) throw new Exception("Data reader is suspended"); + + // Load from wad files + // Note the backward order, because the last wad's images have priority + for (int i = wads.Count - 1; i >= 0; i--) + { + collection = wads[i].LoadFlats(); + AddImagesToList(images, collection); + } + + // Should we load the images in this directory as flats? + if (rootflats) + { + collection = LoadDirectoryImages("", ImageDataFormat.DOOMFLAT, false); + AddImagesToList(images, collection); + } + + // Add images from flats directory + collection = LoadDirectoryImages(FLATS_DIR, ImageDataFormat.DOOMFLAT, true); + AddImagesToList(images, collection); + + // Add images to the container-specific texture set + foreach (ImageData img in images.Values) + textureset.AddFlat(img); + + return new List(images.Values); + } + + #endregion + + #region ================== Colormaps + + // This loads the textures + public override ICollection LoadColormaps() + { + Dictionary images = new Dictionary(); + ICollection collection; + + // Error when suspended + if (issuspended) throw new Exception("Data reader is suspended"); + + // Load from wad files + // Note the backward order, because the last wad's images have priority + for (int i = wads.Count - 1; i >= 0; i--) + { + collection = wads[i].LoadColormaps(); + AddImagesToList(images, collection); + } + + // Add images from flats directory + collection = LoadDirectoryImages(COLORMAPS_DIR, ImageDataFormat.DOOMCOLORMAP, true); + AddImagesToList(images, collection); + + // Add images to the container-specific texture set + foreach (ImageData img in images.Values) + textureset.AddFlat(img); + + return new List(images.Values); + } + + #endregion + + #region ================== Decorate + + // This finds and returns a sprite stream + public override List GetDecorateData(string pname) + { + List streams = new List(); + string[] allfilenames; + + // Error when suspended + if (issuspended) throw new Exception("Data reader is suspended"); + + // Find in root directory + string filename = Path.GetFileName(pname); + string pathname = Path.GetDirectoryName(pname); + + if (filename.IndexOf('.') > -1) + { + allfilenames = new string[1]; + allfilenames[0] = Path.Combine(pathname, filename); + } + else + allfilenames = GetAllFilesWithTitle(pathname, filename, false); + + foreach (string foundfile in allfilenames) + { + streams.Add(LoadFile(foundfile)); + } + + // Find in any of the wad files + for (int i = wads.Count - 1; i >= 0; i--) + streams.AddRange(wads[i].GetDecorateData(pname)); + + return streams; + } + + #endregion + + #region ================== Methods + + // This loads the images in this directory + private ICollection LoadDirectoryImages(string path, int imagetype, bool includesubdirs) + { + List images = new List(); + string[] files; + string name; + + // Go for all files + files = GetAllFiles(path, includesubdirs); + foreach (string f in files) + { + // Make the texture name from filename without extension + name = Path.GetFileNameWithoutExtension(f).ToUpperInvariant(); + if (name.Length > 8) name = name.Substring(0, 8); + if (name.Length > 0) + { + // Add image to list + images.Add(CreateImage(name, f, imagetype)); + } + else + { + // Can't load image without name + General.ErrorLogger.Add(ErrorType.Error, "Can't load an unnamed texture from \"" + path + "\". Please consider giving names to your resources."); + } + } + + // Return result + return images; + } + + // This copies images from a collection unless they already exist in the list + private void AddImagesToList(Dictionary targetlist, ICollection sourcelist) + { + // Go for all source images + foreach (ImageData src in sourcelist) + { + // Check if exists in target list + if (!targetlist.ContainsKey(src.LongName)) + targetlist.Add(src.LongName, src); + } + } + + // This must create an image + protected abstract ImageData CreateImage(string name, string filename, int imagetype); + + // This must return true if the specified file exists + protected abstract bool FileExists(string filename); + + // This must return all files in a given directory + protected abstract string[] GetAllFiles(string path, bool subfolders); + + // This must return all files in a given directory that have the given file title + protected abstract string[] GetAllFilesWithTitle(string path, string title, bool subfolders); + + // This must return all files in a given directory that match the given extension + protected abstract string[] GetFilesWithExt(string path, string extension, bool subfolders); + + // This must find the first file that has the specific name, regardless of file extension + protected abstract string FindFirstFile(string beginswith, bool subfolders); + + // This must find the first file that has the specific name, regardless of file extension + protected abstract string FindFirstFile(string path, string beginswith, bool subfolders); + + // This must find the first file that has the specific name + protected abstract string FindFirstFileWithExt(string path, string beginswith, bool subfolders); + + // This must load an entire file in memory and returns the stream + // NOTE: Callers are responsible for disposing the stream! + protected abstract MemoryStream LoadFile(string filename); + + // This must create a temp file for the speciied file and return the absolute path to the temp file + // NOTE: Callers are responsible for removing the temp file when done! + protected abstract string CreateTempFile(string filename); + + // This makes the path relative to the directory, if needed + protected virtual string MakeRelativePath(string anypath) + { + if (Path.IsPathRooted(anypath)) + { + // Make relative + string lowpath = anypath.ToLowerInvariant(); + string lowlocation = location.location.ToLowerInvariant(); + if ((lowpath.Length > (lowlocation.Length + 1)) && lowpath.StartsWith(lowlocation)) + return anypath.Substring(lowlocation.Length + 1); + else + return anypath; + } + else + { + // Path is already relative + return anypath; + } + } + + #endregion + } +} diff --git a/Source/Core/Data/PatchNames.cs b/Source/Core/Data/PatchNames.cs new file mode 100644 index 0000000..a179e99 --- /dev/null +++ b/Source/Core/Data/PatchNames.cs @@ -0,0 +1,86 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.IO; +using CodeImp.DoomBuilder.IO; + +#endregion + +namespace CodeImp.DoomBuilder.Data +{ + internal sealed class PatchNames + { + #region ================== Constants + + #endregion + + #region ================== Variables + + private string[] pnames; + + #endregion + + #region ================== Properties + + public string this[int index] { get { return pnames[index]; } } + public int Length { get { return pnames.Length; } } + + #endregion + + #region ================== Constructor / Disposer + + // Constructor for empty list + public PatchNames() + { + // Create array + pnames = new string[0]; + } + + // Constructor + public PatchNames(Stream stream) + { + BinaryReader reader = new BinaryReader(stream); + uint length; + + // Read length of array + stream.Seek(0, SeekOrigin.Begin); + length = reader.ReadUInt32(); + + // Create array + pnames = new string[length]; + + // Read all patch names + for (uint i = 0; i < length; i++) + { + byte[] bytes = reader.ReadBytes(8); + pnames[i] = Lump.MakeNormalName(bytes, WAD.ENCODING).ToUpperInvariant(); + } + } + + #endregion + + #region ================== Methods + + #endregion + } +} diff --git a/Source/Core/Data/Playpal.cs b/Source/Core/Data/Playpal.cs new file mode 100644 index 0000000..c528d55 --- /dev/null +++ b/Source/Core/Data/Playpal.cs @@ -0,0 +1,94 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.IO; +using CodeImp.DoomBuilder.Rendering; + +#endregion + +namespace CodeImp.DoomBuilder.Data +{ + public sealed class Playpal + { + #region ================== Constants + + #endregion + + #region ================== Variables + + private PixelColor[] colors; + + #endregion + + #region ================== Properties + + public PixelColor this[int index] { get { return colors[index]; } } + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + public Playpal() + { + // Create array + colors = new PixelColor[256]; + + // Set all palette entries + for (int i = 0; i < 256; i++) + { + // Set colors to gray + colors[i].r = 127; + colors[i].g = 127; + colors[i].b = 127; + colors[i].a = 255; + } + } + + // Constructor + public Playpal(Stream stream) + { + BinaryReader reader = new BinaryReader(stream); + + // Create array + colors = new PixelColor[256]; + + // Read all palette entries + stream.Seek(0, SeekOrigin.Begin); + for (int i = 0; i < 256; i++) + { + // Read colors + colors[i].r = reader.ReadByte(); + colors[i].g = reader.ReadByte(); + colors[i].b = reader.ReadByte(); + colors[i].a = 255; + } + } + + #endregion + + #region ================== Methods + + #endregion + } +} diff --git a/Source/Core/Data/PreviewManager.cs b/Source/Core/Data/PreviewManager.cs new file mode 100644 index 0000000..620e200 --- /dev/null +++ b/Source/Core/Data/PreviewManager.cs @@ -0,0 +1,284 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Drawing; +using System.Drawing.Imaging; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.IO; +using System.IO; +using System.Drawing.Drawing2D; + +#endregion + +namespace CodeImp.DoomBuilder.Data +{ + public class PreviewManager + { + #region ================== Constants + + // Image format + private const PixelFormat IMAGE_FORMAT = PixelFormat.Format32bppArgb; + + // Dimensions of a single preview image + public static readonly int[] PREVIEW_SIZES = new int[] { 48, 64, 80, 96, 112, 128 }; + + #endregion + + #region ================== Variables + + // Dimensions of a single preview image + private int maxpreviewwidth = 64; + private int maxpreviewheight = 64; + + // Images + private List images; + + // Processing + private Queue imageque; + + // Disposing + private bool isdisposed = false; + + #endregion + + #region ================== Properties + + // Constants + public int MaxImageWidth { get { return maxpreviewwidth; } } + public int MaxImageHeight { get { return maxpreviewheight; } } + + // Disposing + internal bool IsDisposed { get { return isdisposed; } } + + // Loading + internal bool IsLoading + { + get + { + return (imageque.Count > 0); + } + } + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + internal PreviewManager() + { + // Initialize + images = new List(); + imageque = new Queue(); + maxpreviewwidth = PREVIEW_SIZES[General.Settings.PreviewImageSize]; + maxpreviewheight = PREVIEW_SIZES[General.Settings.PreviewImageSize]; + + // We have no destructor + GC.SuppressFinalize(this); + } + + // Disposer + internal void Dispose() + { + // Not already disposed? + if (!isdisposed) + { + // Clean up + foreach (Bitmap b in images) b.Dispose(); + images = null; + + // Done + isdisposed = true; + } + } + + #endregion + + #region ================== Private Methods + + // This makes a preview for the given image and updates the image settings + private void MakeImagePreview(ImageData img) + { + int previewwidth, previewheight; + int imagewidth, imageheight; + Bitmap preview; + Graphics g; + + lock (img) + { + // Load image if needed + if (!img.IsImageLoaded) img.LoadImage(); + if (!img.LoadFailed) + { + imagewidth = img.Width; + imageheight = img.Height; + } + else + { + imagewidth = img.GetBitmap().Size.Width; + imageheight = img.GetBitmap().Size.Height; + } + + // Determine preview size + float scalex = (img.Width > maxpreviewwidth) ? ((float)maxpreviewwidth / (float)imagewidth) : 1.0f; + float scaley = (img.Height > maxpreviewheight) ? ((float)maxpreviewheight / (float)imageheight) : 1.0f; + float scale = Math.Min(scalex, scaley); + previewwidth = (int)((float)imagewidth * scale); + previewheight = (int)((float)imageheight * scale); + if (previewwidth < 1) previewwidth = 1; + if (previewheight < 1) previewheight = 1; + + // Make new image + preview = new Bitmap(previewwidth, previewheight, IMAGE_FORMAT); + g = Graphics.FromImage(preview); + g.PageUnit = GraphicsUnit.Pixel; + g.CompositingQuality = CompositingQuality.HighQuality; + g.InterpolationMode = InterpolationMode.HighQualityBicubic; + g.SmoothingMode = SmoothingMode.HighQuality; + g.PixelOffsetMode = PixelOffsetMode.None; + g.Clear(Color.Transparent); + + // Draw image onto atlas + Rectangle atlasrect = new Rectangle(0, 0, previewwidth, previewheight); + RectangleF imgrect = General.MakeZoomedRect(new Size(imagewidth, imageheight), atlasrect); + if (imgrect.Width < 1.0f) + { + imgrect.X -= 0.5f - imgrect.Width * 0.5f; + imgrect.Width = 1.0f; + } + if (imgrect.Height < 1.0f) + { + imgrect.Y -= 0.5f - imgrect.Height * 0.5f; + imgrect.Height = 1.0f; + } + g.DrawImage(img.GetBitmap(), imgrect); + g.Dispose(); + + // Unload image if no longer needed + if (!img.IsReferenced) img.UnloadImage(); + + lock (images) + { + // Set numbers + img.PreviewIndex = images.Count; + img.PreviewState = ImageLoadState.Ready; + + // Add to previews list + images.Add(preview); + } + } + } + + #endregion + + #region ================== Public Methods + + // This draws a preview centered in a target + internal void DrawPreview(int previewindex, Graphics target, Point targetpos) + { + Bitmap image; + + // Get the preview we need + lock (images) { image = images[previewindex]; } + + // Adjust offset for the size of the preview image + targetpos.X += (maxpreviewwidth - image.Width) >> 1; + targetpos.Y += (maxpreviewheight - image.Height) >> 1; + + // Draw from atlas to target + lock (image) + { + target.DrawImageUnscaled(image, targetpos.X, targetpos.Y); + } + } + + // This returns a copy of the preview + internal Bitmap GetPreviewCopy(int previewindex) + { + Bitmap image; + + // Get the preview we need + lock (images) { image = images[previewindex]; } + + // Make a copy + lock (image) + { + return new Bitmap(image); + } + } + + // Background loading + // Return true when we have more work to do, so that the + // thread will not wait too long before calling again + internal bool BackgroundLoad() + { + // Get next item + ImageData image = null; + lock (imageque) + { + // Fetch next image to process + if (imageque.Count > 0) image = imageque.Dequeue(); + } + + // Any image to process? + if (image != null) + { + // Make image preview? + if (!image.IsPreviewLoaded) MakeImagePreview(image); + } + + return (image != null); + } + + // This adds an image for preview creation + internal void AddImage(ImageData image) + { + lock (imageque) + { + // Add to list + image.PreviewState = ImageLoadState.Loading; + imageque.Enqueue(image); + } + } + + +#if DEBUG + internal void DumpAtlases() + { + lock(images) + { + int index = 0; + foreach(Bitmap a in images) + { + lock(a) + { + string file = Path.Combine(General.AppPath, "atlas" + index++ + ".png"); + a.Save(file, ImageFormat.Png); + } + } + } + } +#endif + + #endregion + } +} diff --git a/Source/Core/Data/ResourceImage.cs b/Source/Core/Data/ResourceImage.cs new file mode 100644 index 0000000..340d063 --- /dev/null +++ b/Source/Core/Data/ResourceImage.cs @@ -0,0 +1,100 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Reflection; +using System.Text; +using System.Drawing; +using System.Drawing.Imaging; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.IO; +using System.IO; + +#endregion + +namespace CodeImp.DoomBuilder.Data +{ + public class ResourceImage : ImageData + { + #region ================== Variables + + // Image source + private Assembly assembly; + private string resourcename; + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + public ResourceImage(string resourcename) + { + // Initialize + this.assembly = Assembly.GetCallingAssembly(); + this.resourcename = resourcename; + SetName(resourcename); + + // Temporarily load resource from memory + Stream bitmapdata = assembly.GetManifestResourceStream(resourcename); + Bitmap bmp = (Bitmap)Image.FromStream(bitmapdata); + + // Get width and height from image + width = bmp.Size.Width; + height = bmp.Size.Height; + scale.x = 1.0f; + scale.y = 1.0f; + + // Done + bmp.Dispose(); + bitmapdata.Dispose(); + + // We have no destructor + GC.SuppressFinalize(this); + } + + #endregion + + #region ================== Methods + + // This loads the image + protected override void LocalLoadImage() + { + Stream bitmapdata; + + lock (this) + { + // No failure checking here. I anything fails here, it is not the user's fault, + // because the resources this loads are in the assembly. + + // Get resource from memory + bitmapdata = assembly.GetManifestResourceStream(resourcename); + if (bitmap != null) bitmap.Dispose(); + bitmap = (Bitmap)Image.FromStream(bitmapdata); + bitmapdata.Dispose(); + + // Pass on to base + base.LocalLoadImage(); + } + } + + #endregion + } +} diff --git a/Source/Core/Data/SimpleTextureImage.cs b/Source/Core/Data/SimpleTextureImage.cs new file mode 100644 index 0000000..3594161 --- /dev/null +++ b/Source/Core/Data/SimpleTextureImage.cs @@ -0,0 +1,133 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Drawing; +using System.Drawing.Imaging; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.IO; +using System.IO; + +#endregion + +namespace CodeImp.DoomBuilder.Data +{ + internal class SimpleTextureImage : ImageData + { + #region ================== Constants + + #endregion + + #region ================== Variables + + private string lumpname; + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + public SimpleTextureImage(string name, string lumpname, float scalex, float scaley) + { + // Initialize + this.scale.x = scalex; + this.scale.y = scaley; + this.lumpname = lumpname; + SetName(name); + + // We have no destructor + GC.SuppressFinalize(this); + } + + #endregion + + #region ================== Methods + + // This loads the image + protected override void LocalLoadImage() + { + IImageReader reader; + MemoryStream mem; + Stream patchdata; + byte[] membytes; + + // Checks + if (this.IsImageLoaded) return; + + lock (this) + { + // Get the patch data stream + if (bitmap != null) bitmap.Dispose(); bitmap = null; + patchdata = General.Map.Data.GetTextureData(lumpname); + if (patchdata != null) + { + // Copy patch data to memory + patchdata.Seek(0, SeekOrigin.Begin); + membytes = new byte[(int)patchdata.Length]; + patchdata.Read(membytes, 0, (int)patchdata.Length); + mem = new MemoryStream(membytes); + mem.Seek(0, SeekOrigin.Begin); + + // Get a reader for the data + reader = ImageDataFormat.GetImageReader(mem, ImageDataFormat.DOOMPICTURE, General.Map.Data.Palette); + if (!(reader is UnknownImageReader)) + { + // Load the image + mem.Seek(0, SeekOrigin.Begin); + try { bitmap = reader.ReadAsBitmap(mem); } + catch (InvalidDataException) + { + // Data cannot be read! + bitmap = null; + } + } + + // Not loaded? + if (bitmap == null) + { + General.ErrorLogger.Add(ErrorType.Error, "Image lump '" + lumpname + "' data format could not be read, while loading texture '" + this.Name + "'. Does this lump contain valid picture data at all?"); + loadfailed = true; + } + else + { + // Get width and height from image + width = bitmap.Size.Width; + height = bitmap.Size.Height; + } + + // Done + mem.Dispose(); + } + else + { + General.ErrorLogger.Add(ErrorType.Error, "Image lump '" + lumpname + "' could not be found, while loading texture '" + this.Name + "'. Did you forget to include required resources?"); + loadfailed = true; + } + + // Pass on to base + base.LocalLoadImage(); + } + } + + #endregion + } +} diff --git a/Source/Core/Data/SpriteImage.cs b/Source/Core/Data/SpriteImage.cs new file mode 100644 index 0000000..b36d05a --- /dev/null +++ b/Source/Core/Data/SpriteImage.cs @@ -0,0 +1,142 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Drawing; +using System.Drawing.Imaging; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.IO; +using System.IO; + +#endregion + +namespace CodeImp.DoomBuilder.Data +{ + public sealed class SpriteImage : ImageData + { + #region ================== Variables + + protected int offsetx; + protected int offsety; + + #endregion + + #region ================== Properties + + public int OffsetX { get { return offsetx; } } + public int OffsetY { get { return offsety; } } + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + internal SpriteImage(string name) + { + // Initialize + SetName(name); + + // We have no destructor + GC.SuppressFinalize(this); + } + + #endregion + + #region ================== Methods + + // This loads the image + protected override void LocalLoadImage() + { + Stream lumpdata; + MemoryStream mem; + IImageReader reader; + byte[] membytes; + + // Leave when already loaded + if (this.IsImageLoaded) return; + + lock (this) + { + // Get the lump data stream + lumpdata = General.Map.Data.GetSpriteData(Name); + if (lumpdata != null) + { + // Copy lump data to memory + lumpdata.Seek(0, SeekOrigin.Begin); + membytes = new byte[(int)lumpdata.Length]; + lumpdata.Read(membytes, 0, (int)lumpdata.Length); + mem = new MemoryStream(membytes); + mem.Seek(0, SeekOrigin.Begin); + + // Get a reader for the data + reader = ImageDataFormat.GetImageReader(mem, ImageDataFormat.DOOMPICTURE, General.Map.Data.Palette); + if (reader is UnknownImageReader) + { + // Data is in an unknown format! + General.ErrorLogger.Add(ErrorType.Error, "Sprite lump '" + Name + "' data format could not be read. Does this lump contain valid picture data at all?"); + bitmap = null; + } + else + { + // Read data as bitmap + mem.Seek(0, SeekOrigin.Begin); + if (bitmap != null) bitmap.Dispose(); + bitmap = reader.ReadAsBitmap(mem, out offsetx, out offsety); + } + + // Done + mem.Dispose(); + + if (bitmap != null) + { + // Get width and height from image + width = bitmap.Size.Width; + height = bitmap.Size.Height; + scale.x = 1.0f; + scale.y = 1.0f; + + // Make offset corrections if the offset was not given + if ((offsetx == int.MinValue) || (offsety == int.MinValue)) + { + offsetx = (int)((width * scale.x) * 0.5f); + offsety = (int)(height * scale.y); + } + } + else + { + loadfailed = true; + } + } + else + { + // Missing a patch lump! + General.ErrorLogger.Add(ErrorType.Error, "Missing sprite lump '" + Name + "'. Forgot to include required resources?"); + } + + // Pass on to base + base.LocalLoadImage(); + } + } + + #endregion + } +} diff --git a/Source/Core/Data/TextureImage.cs b/Source/Core/Data/TextureImage.cs new file mode 100644 index 0000000..ec90c00 --- /dev/null +++ b/Source/Core/Data/TextureImage.cs @@ -0,0 +1,168 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Drawing; +using System.Drawing.Imaging; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.IO; +using System.IO; + +#endregion + +namespace CodeImp.DoomBuilder.Data +{ + internal sealed unsafe class TextureImage : ImageData + { + #region ================== Variables + + private List patches; + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + public TextureImage(string name, int width, int height, float scalex, float scaley) + { + // Initialize + this.width = width; + this.height = height; + this.scale.x = scalex; + this.scale.y = scaley; + this.patches = new List(); + SetName(name); + + // We have no destructor + GC.SuppressFinalize(this); + } + + #endregion + + #region ================== Methods + + // This adds a patch to the texture + public void AddPatch(TexturePatch patch) + { + // Add it + patches.Add(patch); + } + + // This loads the image + protected override void LocalLoadImage() + { + IImageReader reader; + BitmapData bitmapdata = null; + MemoryStream mem; + PixelColor* pixels = (PixelColor*)0; + Stream patchdata; + byte[] membytes; + + // Checks + if (this.IsImageLoaded) return; + if ((width == 0) || (height == 0)) return; + + lock (this) + { + // Create texture bitmap + try + { + if (bitmap != null) bitmap.Dispose(); + bitmap = new Bitmap(width, height, PixelFormat.Format32bppArgb); + bitmapdata = bitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb); + pixels = (PixelColor*)bitmapdata.Scan0.ToPointer(); + General.ZeroMemory(new IntPtr(pixels), width * height * sizeof(PixelColor)); + } + catch (Exception e) + { + // Unable to make bitmap + General.ErrorLogger.Add(ErrorType.Error, "Unable to load texture image '" + this.Name + "'. " + e.GetType().Name + ": " + e.Message); + loadfailed = true; + } + + if (!loadfailed) + { + // Go for all patches + foreach (TexturePatch p in patches) + { + // Get the patch data stream + patchdata = General.Map.Data.GetPatchData(p.lumpname); + if (patchdata != null) + { + // Copy patch data to memory + patchdata.Seek(0, SeekOrigin.Begin); + membytes = new byte[(int)patchdata.Length]; + patchdata.Read(membytes, 0, (int)patchdata.Length); + mem = new MemoryStream(membytes); + mem.Seek(0, SeekOrigin.Begin); + + // Get a reader for the data + reader = ImageDataFormat.GetImageReader(mem, ImageDataFormat.DOOMPICTURE, General.Map.Data.Palette); + if (reader is UnknownImageReader) + { + // Data is in an unknown format! + General.ErrorLogger.Add(ErrorType.Error, "Patch lump '" + p.lumpname + "' data format could not be read, while loading texture '" + this.Name + "'. Does this lump contain valid picture data at all?"); + loadfailed = true; + } + else + { + // Draw the patch + mem.Seek(0, SeekOrigin.Begin); + try { reader.DrawToPixelData(mem, pixels, width, height, p.x, p.y); } + catch (InvalidDataException) + { + // Data cannot be read! + General.ErrorLogger.Add(ErrorType.Error, "Patch lump '" + p.lumpname + "' data format could not be read, while loading texture '" + this.Name + "'. Does this lump contain valid picture data at all?"); + loadfailed = true; + } + } + + // Done + mem.Dispose(); + } + else + { + // Missing a patch lump! + General.ErrorLogger.Add(ErrorType.Error, "Missing patch lump '" + p.lumpname + "' while loading texture '" + this.Name + "'. Did you forget to include required resources?"); + loadfailed = true; + } + } + + // Done + bitmap.UnlockBits(bitmapdata); + } + + // Dispose bitmap if load failed + if (loadfailed && (bitmap != null)) + { + bitmap.Dispose(); + bitmap = null; + } + + // Pass on to base + base.LocalLoadImage(); + } + } + + #endregion + } +} diff --git a/Source/Core/Data/TexturePatch.cs b/Source/Core/Data/TexturePatch.cs new file mode 100644 index 0000000..02d5d12 --- /dev/null +++ b/Source/Core/Data/TexturePatch.cs @@ -0,0 +1,87 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Drawing; +using System.Drawing.Imaging; +using System.IO; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Rendering; + +#endregion + +namespace CodeImp.DoomBuilder.Data +{ + internal enum TexturePathRenderStyle + { + Copy, + Blend, + Add, + Subtract, + ReverseSubtract, + Modulate, + CopyAlpha + } + + internal struct TexturePatch + { + public string lumpname; + public int x; + public int y; + public bool flipx; + public bool flipy; + public int rotate; + public PixelColor blend; + public float alpha; + public TexturePathRenderStyle style; + + // Constructor for simple patches + public TexturePatch(string lumpname, int x, int y) + { + // Initialize + this.lumpname = lumpname; + this.x = x; + this.y = y; + this.flipx = false; + this.flipy = false; + this.rotate = 0; + this.blend = new PixelColor(0, 0, 0, 0); + this.alpha = 1.0f; + this.style = TexturePathRenderStyle.Copy; + } + + // Constructor for hires patches + public TexturePatch(string lumpname, int x, int y, bool flipx, bool flipy, int rotate, PixelColor blend, float alpha, int style) + { + // Initialize + this.lumpname = lumpname; + this.x = x; + this.y = y; + this.flipx = flipx; + this.flipy = flipy; + this.rotate = rotate; + this.blend = blend; + this.alpha = alpha; + this.style = (TexturePathRenderStyle)style; + } + } +} diff --git a/Source/Core/Data/UnknownImage.cs b/Source/Core/Data/UnknownImage.cs new file mode 100644 index 0000000..e20840c --- /dev/null +++ b/Source/Core/Data/UnknownImage.cs @@ -0,0 +1,82 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Drawing; +using System.Drawing.Imaging; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.IO; +using System.IO; + +#endregion + +namespace CodeImp.DoomBuilder.Data +{ + public sealed class UnknownImage : ImageData + { + #region ================== Variables + + private Bitmap loadbitmap = null; + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + public UnknownImage(Bitmap image) + { + // Initialize + this.width = 0; + this.height = 0; + this.loadbitmap = image; + SetName(""); + + LocalLoadImage(); + + // We have no destructor + GC.SuppressFinalize(this); + } + + #endregion + + #region ================== Methods + + // This 'loads' the image + protected override void LocalLoadImage() + { + bitmap = loadbitmap; + base.LocalLoadImage(); + } + + // This returns a preview image + public override Image GetPreview() + { + lock (this) + { + // Make a copy + return new Bitmap(loadbitmap); + } + } + + #endregion + } +} diff --git a/Source/Core/Data/WADReader.cs b/Source/Core/Data/WADReader.cs new file mode 100644 index 0000000..b118055 --- /dev/null +++ b/Source/Core/Data/WADReader.cs @@ -0,0 +1,846 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Drawing; +using System.Drawing.Imaging; +using System.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.ZDoom; +using CodeImp.DoomBuilder.Rendering; + +#endregion + +namespace CodeImp.DoomBuilder.Data +{ + internal sealed class WADReader : DataReader + { + #region ================== Constants + + #endregion + + #region ================== Structures + + private struct LumpRange + { + public int start; + public int end; + } + + #endregion + + #region ================== Variables + + // Source + private WAD file; + private bool is_iwad; + private bool strictpatches; + + // Lump ranges + private List flatranges; + private List patchranges; + private List spriteranges; + private List textureranges; + private List colormapranges; + + #endregion + + #region ================== Properties + + public bool IsIWAD { get { return is_iwad; } } + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + public WADReader(DataLocation dl) : base(dl) + { + General.WriteLogLine("Opening WAD resource '" + location.location + "'"); + + // Initialize + file = new WAD(location.location, true); + is_iwad = (file.Type == WAD.TYPE_IWAD); + strictpatches = dl.option1; + patchranges = new List(); + spriteranges = new List(); + flatranges = new List(); + textureranges = new List(); + colormapranges = new List(); + + // Find ranges + FindRanges(patchranges, General.Map.Config.PatchRanges, "patches"); + FindRanges(spriteranges, General.Map.Config.SpriteRanges, "sprites"); + FindRanges(flatranges, General.Map.Config.FlatRanges, "flats"); + FindRanges(textureranges, General.Map.Config.TextureRanges, "textures"); + FindRanges(colormapranges, General.Map.Config.ColormapRanges, "colormaps"); + + // We have no destructor + GC.SuppressFinalize(this); + } + + // Disposer + public override void Dispose() + { + // Not already disposed? + if (!isdisposed) + { + General.WriteLogLine("Closing WAD resource '" + location.location + "'"); + + // Clean up + file.Dispose(); + + // Done + base.Dispose(); + } + } + + #endregion + + #region ================== Management + + // Return a short name for this data location + public override string GetTitle() + { + return Path.GetFileName(location.location); + } + + // This suspends use of this resource + public override void Suspend() + { + file.Dispose(); + base.Suspend(); + } + + // This resumes use of this resource + public override void Resume() + { + file = new WAD(location.location, true); + is_iwad = (file.Type == WAD.TYPE_IWAD); + base.Resume(); + } + + // This fills a ranges list + private void FindRanges(List ranges, IDictionary rangeinfos, string rangename) + { + foreach (DictionaryEntry r in rangeinfos) + { + // Read start and end + string rangestart = General.Map.Config.ReadSetting(rangename + "." + r.Key + ".start", ""); + string rangeend = General.Map.Config.ReadSetting(rangename + "." + r.Key + ".end", ""); + if ((rangestart.Length > 0) && (rangeend.Length > 0)) + { + // Find ranges + int startindex = file.FindLumpIndex(rangestart); + while (startindex > -1) + { + LumpRange range = new LumpRange(); + range.start = startindex; + range.end = file.FindLumpIndex(rangeend, startindex); + if (range.end > -1) + { + ranges.Add(range); + startindex = file.FindLumpIndex(rangestart, range.end); + } + else + { + startindex = -1; + } + } + } + } + } + + #endregion + + #region ================== Palette + + // This loads the PLAYPAL palette + public override Playpal LoadPalette() + { + Lump lump; + + // Error when suspended + if (issuspended) throw new Exception("Data reader is suspended"); + + // Look for a lump named PLAYPAL + lump = file.FindLump("PLAYPAL"); + if (lump != null) + { + // Read the PLAYPAL from stream + return new Playpal(lump.Stream); + } + else + { + // No palette + return null; + } + } + + // villsa + public override Playpal LoadThingPalette(string palname) + { + Lump lump; + + // Error when suspended + if (issuspended) throw new Exception("Data reader is suspended"); + + // Look for a lump named PLAYPAL + lump = file.FindLump(palname); + if (lump != null) + { + // Read the PLAYPAL from stream + return new Playpal(lump.Stream); + } + else + { + // No palette + return null; + } + } + + #endregion + + #region ================== Colormaps + + // This loads the textures + public override ICollection LoadColormaps() + { + List images = new List(); + string rangestart, rangeend; + + // Error when suspended + if (issuspended) throw new Exception("Data reader is suspended"); + + // Read ranges from configuration + foreach (DictionaryEntry r in General.Map.Config.ColormapRanges) + { + // Read start and end + rangestart = General.Map.Config.ReadSetting("colormaps." + r.Key + ".start", ""); + rangeend = General.Map.Config.ReadSetting("colormaps." + r.Key + ".end", ""); + if ((rangestart.Length > 0) && (rangeend.Length > 0)) + { + // Load texture range + LoadColormapsRange(rangestart, rangeend, ref images); + } + } + + // Add images to the container-specific texture set + foreach (ImageData img in images) + textureset.AddFlat(img); + + // Return result + return images; + } + + // This loads a range of colormaps + private void LoadColormapsRange(string startlump, string endlump, ref List images) + { + int startindex, endindex; + float defaultscale; + ColormapImage image; + + // Determine default scale + defaultscale = General.Map.Config.DefaultTextureScale; + + // Continue until no more start can be found + startindex = file.FindLumpIndex(startlump); + while (startindex > -1) + { + // Find end index + endindex = file.FindLumpIndex(endlump, startindex + 1); + if (endindex > -1) + { + // Go for all lumps between start and end exclusive + for (int i = startindex + 1; i < endindex; i++) + { + // Lump not zero-length? + if (file.Lumps[i].Length > 0) + { + // Make the image object + image = new ColormapImage(file.Lumps[i].Name); + + // Add image to collection + images.Add(image); + } + } + } + + // Find the next start + startindex = file.FindLumpIndex(startlump, startindex + 1); + } + } + + // This finds and returns a colormap stream + public override Stream GetColormapData(string pname) + { + Lump lump; + + // Error when suspended + if (issuspended) throw new Exception("Data reader is suspended"); + + // Strictly read patches only between C_START and C_END? + if (strictpatches) + { + // Find the lump in ranges + foreach (LumpRange range in colormapranges) + { + lump = file.FindLump(pname, range.start, range.end); + if (lump != null) return lump.Stream; + } + } + else + { + // Find the lump anywhere + lump = file.FindLump(pname); + if (lump != null) return lump.Stream; + } + + return null; + } + + #endregion + + #region ================== Textures + + // villsa + private uint HashTextureName(string name) + { + uint hash = 1315423911; + int j = 0; + + for (uint i = 0; i < name.Length && name[j] != '\0'; j++, i++) + { + string c = (name[j].ToString()).ToUpper(); + hash ^= ((hash << 5) + c[0] + (hash >> 2)); + } + + return hash % 65536; + } + + // This loads the textures + public override ICollection LoadTextures(PatchNames pnames) + { + List images = new List(); + string rangestart, rangeend; + int lumpindex; + Lump lump; + + // Error when suspended + if (issuspended) throw new Exception("Data reader is suspended"); + + // Load two sets of textures, if available + lump = file.FindLump("TEXTURE1"); + if (lump != null) LoadTextureSet("TEXTURE1", lump.Stream, ref images, pnames); + lump = file.FindLump("TEXTURE2"); + if (lump != null) LoadTextureSet("TEXTURE2", lump.Stream, ref images, pnames); + + // Read ranges from configuration + foreach (LumpRange range in textureranges) + { + // Load texture range + LoadTexturesRange(range.start, range.end, ref images, pnames); + } + + foreach (Sector s in General.Map.Map.Sectors) + { + for (int j = 0; j < General.Map.TextureHashKey.Count; j++) + { + if (s.HashFloor == General.Map.TextureHashKey[j]) + { + s.SetFloorTexture(General.Map.TextureHashName[j]); + break; + } + } + + for (int j = 0; j < General.Map.TextureHashKey.Count; j++) + { + if (s.HashCeiling == General.Map.TextureHashKey[j]) + { + s.SetCeilTexture(General.Map.TextureHashName[j]); + break; + } + } + } + + foreach (Sidedef sd in General.Map.Map.Sidedefs) + { + for (int j = 0; j < General.Map.TextureHashKey.Count; j++) + { + if (sd.HashTexHigh == General.Map.TextureHashKey[j]) + { + if (j == 0) + { + sd.SetTextureHigh("-"); + break; + } + + sd.SetTextureHigh(General.Map.TextureHashName[j]); + break; + } + } + + for (int j = 0; j < General.Map.TextureHashKey.Count; j++) + { + if (sd.HashTexMid == General.Map.TextureHashKey[j]) + { + if (j == 0) + { + sd.SetTextureMid("-"); + break; + } + + sd.SetTextureMid(General.Map.TextureHashName[j]); + break; + } + } + + for (int j = 0; j < General.Map.TextureHashKey.Count; j++) + { + if (sd.HashTexLow == General.Map.TextureHashKey[j]) + { + if (j == 0) + { + sd.SetTextureLow("-"); + break; + } + + sd.SetTextureLow(General.Map.TextureHashName[j]); + break; + } + } + } + + // Load TEXTURES lump file + lumpindex = file.FindLumpIndex("TEXTURES"); + while (lumpindex > -1) + { + MemoryStream filedata = new MemoryStream(file.Lumps[lumpindex].Stream.ReadAllBytes()); + WADReader.LoadHighresTextures(filedata, "TEXTURES", ref images, null, null); + filedata.Dispose(); + + // Find next + lumpindex = file.FindLumpIndex("TEXTURES", lumpindex + 1); + } + + // Add images to the container-specific texture set + foreach (ImageData img in images) + textureset.AddTexture(img); + + // Return result + return images; + } + + // This loads a range of textures + private void LoadTexturesRange(int startindex, int endindex, ref List images, PatchNames pnames) + { + // Determine default scale + float defaultscale = General.Map.Config.DefaultTextureScale; + + // Go for all lumps between start and end exclusive + for (int i = startindex + 1; i < endindex; i++) + { + // Lump not zero length? + if (file.Lumps[i].Length > 0) + { + uint hash; + + // Make the image + SimpleTextureImage image = new SimpleTextureImage(file.Lumps[i].Name, file.Lumps[i].Name, defaultscale, defaultscale); + + // Add image to collection + images.Add(image); + + // villsa + hash = HashTextureName(file.Lumps[i].Name); + General.Map.TextureHashKey.Add(hash); + General.Map.TextureHashName.Add(file.Lumps[i].Name); + } + else + { + // Can't load image without name + General.ErrorLogger.Add(ErrorType.Error, "Can't load an unnamed texture from lump index " + i + ". Please consider giving names to your resources."); + } + } + } + + // This loads the texture definitions from a TEXTURES lump + public static void LoadHighresTextures(Stream stream, string filename, ref List images, Dictionary textures, Dictionary flats) + { + // Parse the data + TexturesParser parser = new TexturesParser(); + parser.Parse(stream, filename); + + // Make the textures + foreach (TextureStructure t in parser.Textures) + { + if (t.Name.Length > 0) + { + // Add the texture + ImageData img = t.MakeImage(textures, flats); + images.Add(img); + } + else + { + // Can't load image without name + General.ErrorLogger.Add(ErrorType.Error, "Can't load an unnamed texture from \"" + filename + "\". Please consider giving names to your resources."); + } + } + } + + // This loads a set of textures + public static void LoadTextureSet(string sourcename, Stream texturedata, ref List images, PatchNames pnames) + { + BinaryReader reader = new BinaryReader(texturedata); + int flags, width, height, patches, px, py, pi; + uint numtextures; + byte scalebytex, scalebytey; + float scalex, scaley, defaultscale; + byte[] namebytes; + TextureImage image = null; + bool strifedata; + + if (texturedata.Length == 0) + return; + + // Determine default scale + defaultscale = General.Map.Config.DefaultTextureScale; + + // Get number of textures + texturedata.Seek(0, SeekOrigin.Begin); + numtextures = reader.ReadUInt32(); + + // Skip offset bytes (we will read all textures sequentially) + texturedata.Seek(4 * numtextures, SeekOrigin.Current); + + // Go for all textures defined in this lump + for (uint i = 0; i < numtextures; i++) + { + // Read texture properties + namebytes = reader.ReadBytes(8); + flags = reader.ReadUInt16(); + scalebytex = reader.ReadByte(); + scalebytey = reader.ReadByte(); + width = reader.ReadInt16(); + height = reader.ReadInt16(); + patches = reader.ReadInt16(); + + // Check for doom or strife data format + if (patches == 0) + { + // Ignore 2 bytes and then read number of patches + texturedata.Seek(2, SeekOrigin.Current); + patches = reader.ReadInt16(); + strifedata = false; + } + else + { + // Texture data is in strife format + strifedata = true; + } + + // Determine actual scales + if (scalebytex == 0) scalex = defaultscale; else scalex = 1f / ((float)scalebytex / 8f); + if (scalebytey == 0) scaley = defaultscale; else scaley = 1f / ((float)scalebytey / 8f); + + // Validate data + if ((width > 0) && (height > 0) && (patches > 0) && + (scalex != 0) || (scaley != 0)) + { + string texname = Lump.MakeNormalName(namebytes, WAD.ENCODING); + if (texname.Length > 0) + { + // Make the image object + image = new TextureImage(Lump.MakeNormalName(namebytes, WAD.ENCODING), + width, height, scalex, scaley); + } + else + { + // Can't load image without name + General.ErrorLogger.Add(ErrorType.Error, "Can't load an unnamed texture from \"" + sourcename + "\". Please consider giving names to your resources."); + } + + // Go for all patches in texture + for (int p = 0; p < patches; p++) + { + // Read patch properties + px = reader.ReadInt16(); + py = reader.ReadInt16(); + pi = reader.ReadUInt16(); + if (!strifedata) texturedata.Seek(4, SeekOrigin.Current); + + // Validate data + if ((pi >= 0) && (pi < pnames.Length)) + { + if (pnames[pi].Length > 0) + { + // Create patch on image + if (image != null) image.AddPatch(new TexturePatch(pnames[pi], px, py)); + } + else + { + // Can't load image without name + General.ErrorLogger.Add(ErrorType.Error, "Can't use an unnamed patch referenced in \"" + sourcename + "\". Please consider giving names to your resources."); + } + } + } + + // Add image to collection + images.Add(image); + } + else + { + // Skip patches data + texturedata.Seek(6 * patches, SeekOrigin.Current); + if (!strifedata) texturedata.Seek(4 * patches, SeekOrigin.Current); + } + } + } + + // This returns the patch names from the PNAMES lump + public override PatchNames LoadPatchNames() + { + Lump lump; + + // Error when suspended + if (issuspended) throw new Exception("Data reader is suspended"); + + // Look for a lump named PNAMES + lump = file.FindLump("PNAMES"); + if (lump != null) + { + // Read the PNAMES from stream + return new PatchNames(lump.Stream); + } + else + { + // No palette + return null; + } + } + + // This finds and returns a patch stream + public override Stream GetPatchData(string pname) + { + Lump lump; + + // Error when suspended + if (issuspended) throw new Exception("Data reader is suspended"); + + // Strictly read patches only between P_START and P_END? + if (strictpatches) + { + // Find the lump in ranges + foreach (LumpRange range in patchranges) + { + lump = file.FindLump(pname, range.start, range.end); + if (lump != null) return lump.Stream; + } + } + else + { + // Find the lump anywhere + lump = file.FindLump(pname); + if (lump != null) return lump.Stream; + } + + return null; + } + + // This finds and returns a texture stream + public override Stream GetTextureData(string pname) + { + Lump lump; + + // Error when suspended + if (issuspended) throw new Exception("Data reader is suspended"); + + // Find the lump in ranges + foreach (LumpRange range in textureranges) + { + lump = file.FindLump(pname, range.start, range.end); + if (lump != null) return lump.Stream; + } + + return null; + } + + #endregion + + #region ================== Flats + + // This loads the textures + public override ICollection LoadFlats() + { + List images = new List(); + string rangestart, rangeend; + + // Error when suspended + if (issuspended) throw new Exception("Data reader is suspended"); + + // Read ranges from configuration + foreach (DictionaryEntry r in General.Map.Config.FlatRanges) + { + // Read start and end + rangestart = General.Map.Config.ReadSetting("flats." + r.Key + ".start", ""); + rangeend = General.Map.Config.ReadSetting("flats." + r.Key + ".end", ""); + if ((rangestart.Length > 0) && (rangeend.Length > 0)) + { + // Load texture range + LoadFlatsRange(rangestart, rangeend, ref images); + } + } + + // Add images to the container-specific texture set + foreach (ImageData img in images) + textureset.AddFlat(img); + + // Return result + return images; + } + + // This loads a range of flats + private void LoadFlatsRange(string startlump, string endlump, ref List images) + { + int startindex, endindex; + float defaultscale; + FlatImage image; + + // Determine default scale + defaultscale = General.Map.Config.DefaultTextureScale; + + // Continue until no more start can be found + startindex = file.FindLumpIndex(startlump); + while (startindex > -1) + { + // Find end index + endindex = file.FindLumpIndex(endlump, startindex + 1); + if (endindex > -1) + { + // Go for all lumps between start and end exclusive + for (int i = startindex + 1; i < endindex; i++) + { + // Lump not zero-length? + if (file.Lumps[i].Length > 0) + { + // Make the image object + image = new FlatImage(file.Lumps[i].Name); + + // Add image to collection + images.Add(image); + } + } + } + + // Find the next start + startindex = file.FindLumpIndex(startlump, startindex + 1); + } + } + + // This finds and returns a patch stream + public override Stream GetFlatData(string pname) + { + Lump lump; + + // Error when suspended + if (issuspended) throw new Exception("Data reader is suspended"); + + // Find the lump in ranges + foreach (LumpRange range in flatranges) + { + lump = file.FindLump(pname, range.start, range.end); + if (lump != null) return lump.Stream; + } + + return null; + } + + #endregion + + #region ================== Sprite + + // This finds and returns a sprite stream + public override Stream GetSpriteData(string pname) + { + Lump lump; + + // Error when suspended + if (issuspended) throw new Exception("Data reader is suspended"); + + // Find the lump in ranges + foreach (LumpRange range in spriteranges) + { + lump = file.FindLump(pname, range.start, range.end); + if (lump != null) return lump.Stream; + } + + return null; + } + + // This checks if the given sprite exists + public override bool GetSpriteExists(string pname) + { + Lump lump; + + // Error when suspended + if (issuspended) throw new Exception("Data reader is suspended"); + + // Find the lump in ranges + foreach (LumpRange range in spriteranges) + { + lump = file.FindLump(pname, range.start, range.end); + if (lump != null) return true; + } + + return false; + } + + #endregion + + #region ================== Things + + // This finds and returns a sprite stream + public override List GetDecorateData(string pname) + { + List streams = new List(); + int lumpindex; + + // Error when suspended + if (issuspended) throw new Exception("Data reader is suspended"); + + // Find all lumps named 'DECORATE' + lumpindex = file.FindLumpIndex(pname); + while (lumpindex > -1) + { + streams.Add(file.Lumps[lumpindex].Stream); + + // Find next + lumpindex = file.FindLumpIndex(pname, lumpindex + 1); + } + + return streams; + } + + #endregion + } +} diff --git a/Source/Core/Editing/ClassicMode.cs b/Source/Core/Editing/ClassicMode.cs new file mode 100644 index 0000000..bc028bc --- /dev/null +++ b/Source/Core/Editing/ClassicMode.cs @@ -0,0 +1,807 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using CodeImp.DoomBuilder.Windows; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Actions; +using CodeImp.DoomBuilder.Geometry; +using System.Drawing; + +#endregion + +namespace CodeImp.DoomBuilder.Editing +{ + /// + /// Provides specialized functionality for a classic (2D) Doom Builder editing mode. + /// + public abstract class ClassicMode : EditMode + { + #region ================== Constants + + private const float SCALE_MAX = 20f; + private const float SCALE_MIN = 0.01f; + private const float SELECTION_BORDER_SIZE = 2f; + private const int SELECTION_ALPHA = 200; + private const float CENTER_VIEW_PADDING = 0.06f; + private const float AUTOPAN_BORDER_SIZE = 100.0f; + + #endregion + + #region ================== Variables + + // Cancelled? + protected bool cancelled; + + // Graphics + protected IRenderer2D renderer; + private Renderer2D renderer2d; + + // Mouse status + protected Vector2D mousepos; + protected Vector2D mouselastpos; + protected Vector2D mousemappos; + protected Vector2D mousedownpos; + protected Vector2D mousedownmappos; + protected MouseButtons mousebuttons; + protected bool mouseinside; + protected MouseButtons mousedragging = MouseButtons.None; + + // Selection + protected bool selecting; + private Vector2D selectstart; + protected RectangleF selectionrect; + + // View panning + protected bool panning; + private bool autopanenabled; + + #endregion + + #region ================== Properties + + // Mouse status + public Vector2D MousePos { get { return mousepos; } } + public Vector2D MouseLastPos { get { return mouselastpos; } } + public Vector2D MouseMapPos { get { return mousemappos; } } + public Vector2D MouseDownPos { get { return mousedownpos; } } + public Vector2D MouseDownMapPos { get { return mousedownmappos; } } + public MouseButtons MouseButtons { get { return mousebuttons; } } + public bool IsMouseInside { get { return mouseinside; } } + public MouseButtons MouseDragging { get { return mousedragging; } } + + // Selection + public bool IsSelecting { get { return selecting; } } + public Vector2D SelectionStart { get { return selectstart; } } + public RectangleF SelectionRect { get { return selectionrect; } } + + // Panning + public bool IsPanning { get { return panning; } } + + #endregion + + #region ================== Constructor / Disposer + + /// + /// Provides specialized functionality for a classic (2D) Doom Builder editing mode. + /// + public ClassicMode() + { + // Initialize + this.renderer = General.Map.Renderer2D; + this.renderer2d = (Renderer2D)General.Map.Renderer2D; + + // If the current mode is a ClassicMode, copy mouse properties + if (General.Editing.Mode is ClassicMode) + { + ClassicMode oldmode = General.Editing.Mode as ClassicMode; + + // Copy mouse properties + mousepos = oldmode.mousepos; + mousemappos = oldmode.mousemappos; + mousedownpos = oldmode.mousedownpos; + mousedownmappos = oldmode.mousedownmappos; + mousebuttons = oldmode.mousebuttons; + mouseinside = oldmode.mouseinside; + mousedragging = oldmode.mousedragging; + } + } + + // Disposer + public override void Dispose() + { + // Not already disposed? + if (!isdisposed) + { + // Clean up + + // Dispose base + base.Dispose(); + } + } + + #endregion + + #region ================== Scroll / Zoom + + // This scrolls the view north + [BeginAction("scrollnorth", BaseAction = true)] + public virtual void ScrollNorth() + { + // Scroll + ScrollBy(0f, 100f / renderer2d.Scale); + } + + // This scrolls the view south + [BeginAction("scrollsouth", BaseAction = true)] + public virtual void ScrollSouth() + { + // Scroll + ScrollBy(0f, -100f / renderer2d.Scale); + } + + // This scrolls the view west + [BeginAction("scrollwest", BaseAction = true)] + public virtual void ScrollWest() + { + // Scroll + ScrollBy(-100f / renderer2d.Scale, 0f); + } + + // This scrolls the view east + [BeginAction("scrolleast", BaseAction = true)] + public virtual void ScrollEast() + { + // Scroll + ScrollBy(100f / renderer2d.Scale, 0f); + } + + // This zooms in + [BeginAction("zoomin", BaseAction = true)] + public virtual void ZoomIn() + { + float z = 1.0f + General.Settings.ZoomFactor * 0.1f; + + // Zoom + ZoomBy(z); + } + + // This zooms out + [BeginAction("zoomout", BaseAction = true)] + public virtual void ZoomOut() + { + float z = 1.0f + General.Settings.ZoomFactor * 0.1f; + + // Zoom + ZoomBy(1.0f / z); + } + + // This scrolls anywhere + private void ScrollBy(float deltax, float deltay) + { + // Scroll now + renderer2d.PositionView(renderer2d.OffsetX + deltax, renderer2d.OffsetY + deltay); + this.OnViewChanged(); + + // Redraw + General.MainWindow.RedrawDisplay(); + + // Determine new unprojected mouse coordinates + mousemappos = renderer2d.DisplayToMap(mousepos); + General.MainWindow.UpdateCoordinates(mousemappos); + } + + // This sets the view to be centered at x,y + private void ScrollTo(float x, float y) + { + // Scroll now + renderer2d.PositionView(x, y); + this.OnViewChanged(); + + // Redraw + General.MainWindow.RedrawDisplay(); + + // Determine new unprojected mouse coordinates + mousemappos = renderer2d.DisplayToMap(mousepos); + General.MainWindow.UpdateCoordinates(mousemappos); + } + + // This zooms + private void ZoomBy(float deltaz) + { + Vector2D zoompos, clientsize, diff; + float newscale; + + // This will be the new zoom scale + newscale = renderer2d.Scale * deltaz; + + // Limit scale + if (newscale > SCALE_MAX) newscale = SCALE_MAX; + if (newscale < SCALE_MIN) newscale = SCALE_MIN; + + // Get the dimensions of the display + clientsize = new Vector2D(General.Map.Graphics.RenderTarget.ClientSize.Width, + General.Map.Graphics.RenderTarget.ClientSize.Height); + + // When mouse is inside display + if (mouseinside) + { + // Zoom into or from mouse position + zoompos = (mousepos / clientsize) - new Vector2D(0.5f, 0.5f); + } + else + { + // Zoom into or from center + zoompos = new Vector2D(0f, 0f); + } + + // Calculate view position difference + diff = ((clientsize / newscale) - (clientsize / renderer2d.Scale)) * zoompos; + + // Zoom now + renderer2d.PositionView(renderer2d.OffsetX - diff.x, renderer2d.OffsetY + diff.y); + renderer2d.ScaleView(newscale); + this.OnViewChanged(); + + // Redraw + //General.Map.Map.Update(); + General.MainWindow.RedrawDisplay(); + + // Give a new mousemove event to update coordinates + if (mouseinside) OnMouseMove(new MouseEventArgs(mousebuttons, 0, (int)mousepos.x, (int)mousepos.y, 0)); + } + + // This zooms to a specific level + public void SetZoom(float newscale) + { + // Zoom now + renderer2d.ScaleView(newscale); + this.OnViewChanged(); + + // Redraw + //General.Map.Map.Update(); + General.MainWindow.RedrawDisplay(); + + // Give a new mousemove event to update coordinates + if (mouseinside) OnMouseMove(new MouseEventArgs(mousebuttons, 0, (int)mousepos.x, (int)mousepos.y, 0)); + } + + // This zooms and scrolls to fit the map in the window + [BeginAction("centerinscreen", BaseAction = true)] + public void CenterInScreen() + { + float left = float.MaxValue; + float top = float.MaxValue; + float right = float.MinValue; + float bottom = float.MinValue; + bool anything = false; + + // Go for all vertices + foreach (Vertex v in General.Map.Map.Vertices) + { + // Vertex used? + if (v.Linedefs.Count > 0) + { + // Adjust boundaries by vertices + if (v.Position.x < left) left = v.Position.x; + if (v.Position.x > right) right = v.Position.x; + if (v.Position.y < top) top = v.Position.y; + if (v.Position.y > bottom) bottom = v.Position.y; + anything = true; + } + } + + // Not already found something to center in view? + if (!anything) + { + // Go for all things + foreach (Thing t in General.Map.Map.Things) + { + // Adjust boundaries by vertices + if (t.Position.x < left) left = t.Position.x; + if (t.Position.x > right) right = t.Position.x; + if (t.Position.y < top) top = t.Position.y; + if (t.Position.y > bottom) bottom = t.Position.y; + anything = true; + } + } + + // Anything found to center in view? + if (anything) + { + RectangleF area = new RectangleF(left, top, (right - left), (bottom - top)); + CenterOnArea(area, CENTER_VIEW_PADDING); + } + else + { + // Default view + SetDefaultZoom(); + } + } + + // This zooms and moves to view the given area + public void CenterOnArea(RectangleF area, float padding) + { + float scalew, scaleh, scale; + + // Add size to the area for better overview + area.Inflate(area.Width * padding, area.Height * padding); + + // Calculate scale to view map at + scalew = (float)General.Map.Graphics.RenderTarget.ClientSize.Width / area.Width; + scaleh = (float)General.Map.Graphics.RenderTarget.ClientSize.Height / area.Height; + if (scalew < scaleh) scale = scalew; else scale = scaleh; + + // Change the view to see the whole map + renderer2d.ScaleView(scale); + renderer2d.PositionView(area.Left + area.Width * 0.5f, area.Top + area.Height * 0.5f); + this.OnViewChanged(); + + // Redraw + General.MainWindow.RedrawDisplay(); + + // Give a new mousemove event to update coordinates + if (mouseinside) OnMouseMove(new MouseEventArgs(mousebuttons, 0, (int)mousepos.x, (int)mousepos.y, 0)); + } + + // This sets up the default view + public void SetDefaultZoom() + { + // View middle of map at 50% zoom + renderer2d.ScaleView(0.5f); + renderer2d.PositionView(0.0f, 0.0f); + this.OnViewChanged(); + General.MainWindow.RedrawDisplay(); + + // Give a new mousemove event to update coordinates + if (mouseinside) OnMouseMove(new MouseEventArgs(mousebuttons, 0, (int)mousepos.x, (int)mousepos.y, 0)); + } + + /// + /// This is called when the view changes (scroll/zoom), before the display is redrawn. + /// + protected virtual void OnViewChanged() + { + } + + // This enabled automatic panning, if preferred + protected void EnableAutoPanning() + { + if (General.Settings.AutoScrollSpeed > 0) + { + if (!autopanenabled) + { + autopanenabled = true; + General.MainWindow.EnableProcessing(); + } + } + } + + // This disabls automatic panning + protected void DisableAutoPanning() + { + if (autopanenabled) + { + autopanenabled = false; + General.MainWindow.DisableProcessing(); + } + } + + #endregion + + #region ================== Processing + + // Processing + public override void OnProcess(double deltatime) + { + base.OnProcess(deltatime); + + if (autopanenabled) + { + Vector2D panamount = new Vector2D(); + + // How much to pan the view in X? + if (mousepos.x < AUTOPAN_BORDER_SIZE) + panamount.x = -AUTOPAN_BORDER_SIZE + mousepos.x; + else if (mousepos.x > (General.MainWindow.Display.ClientSize.Width - AUTOPAN_BORDER_SIZE)) + panamount.x = mousepos.x - (General.MainWindow.Display.ClientSize.Width - AUTOPAN_BORDER_SIZE); + + // How much to pan the view in Y? + if (mousepos.y < AUTOPAN_BORDER_SIZE) + panamount.y = AUTOPAN_BORDER_SIZE - mousepos.y; + else if (mousepos.y > (General.MainWindow.Display.ClientSize.Height - AUTOPAN_BORDER_SIZE)) + panamount.y = -(mousepos.y - (General.MainWindow.Display.ClientSize.Height - AUTOPAN_BORDER_SIZE)); + + // Do any panning? + if (panamount.GetManhattanLength() > 0.0f) + { + // Scale and power this for nicer usability + Vector2D pansign = panamount.GetSign(); + panamount = (panamount * panamount) * pansign * 0.0001f * (float)General.Settings.AutoScrollSpeed / renderer.Scale; + + // Multiply by delta time + panamount.x = (float)((double)panamount.x * deltatime); + panamount.y = (float)((double)panamount.y * deltatime); + + // Pan the view + ScrollBy(panamount.x, panamount.y); + } + } + } + + #endregion + + #region ================== Input + + // Mouse leaves the display + public override void OnMouseLeave(EventArgs e) + { + // Mouse is outside the display + mouseinside = false; + mousepos = new Vector2D(float.NaN, float.NaN); + mousemappos = mousepos; + mousebuttons = MouseButtons.None; + + // Determine new unprojected mouse coordinates + General.MainWindow.UpdateCoordinates(mousemappos); + + // Let the base class know + base.OnMouseLeave(e); + } + + // Mouse moved inside the display + public override void OnMouseMove(MouseEventArgs e) + { + Vector2D delta; + + // Record last position + mouseinside = true; + mouselastpos = mousepos; + mousepos = new Vector2D(e.X, e.Y); + mousemappos = renderer2d.DisplayToMap(mousepos); + mousebuttons = e.Button; + + // Update labels in main window + General.MainWindow.UpdateCoordinates(mousemappos); + + // Holding a button? + if (e.Button != MouseButtons.None) + { + // Not dragging? + if (mousedragging == MouseButtons.None) + { + // Check if moved enough pixels for dragging + delta = mousedownpos - mousepos; + if ((Math.Abs(delta.x) > DRAG_START_MOVE_PIXELS) || + (Math.Abs(delta.y) > DRAG_START_MOVE_PIXELS)) + { + // Dragging starts now + mousedragging = e.Button; + OnDragStart(e); + } + } + } + + // Selecting? + if (selecting) OnUpdateMultiSelection(); + + // Panning? + if (panning) OnUpdateViewPanning(); + + // Let the base class know + base.OnMouseMove(e); + } + + // Mouse button pressed + public override void OnMouseDown(MouseEventArgs e) + { + // Save mouse down position + mousedownpos = mousepos; + mousedownmappos = mousemappos; + + // Let the base class know + base.OnMouseDown(e); + } + + // Mouse button released + public override void OnMouseUp(MouseEventArgs e) + { + // Releasing drag button? + if (e.Button == mousedragging) + { + // No longer dragging + OnDragStop(e); + mousedragging = MouseButtons.None; + } + + // Let the base class know + base.OnMouseUp(e); + } + + /// + /// Automatically called when dragging operation starts. + /// + protected virtual void OnDragStart(MouseEventArgs e) + { + } + + /// + /// Automatically called when dragging operation stops. + /// + protected virtual void OnDragStop(MouseEventArgs e) + { + } + + #endregion + + #region ================== Display + + // This just refreshes the display + public override void OnPresentDisplay() + { + renderer2d.Present(); + } + + // This sets the view mode + private void SetViewMode(ViewMode mode) + { + General.Map.CRenderer2D.SetViewMode(mode); + General.MainWindow.UpdateInterface(); + General.MainWindow.RedrawDisplay(); + } + + #endregion + + #region ================== Methods + + /// + /// Automatically called by the core when this editing mode is engaged. + /// + public override void OnEngage() + { + // Clear display overlay + renderer.StartOverlay(true); + renderer.Finish(); + base.OnEngage(); + } + + /// + /// Called when the user requests to cancel this editing mode. + /// + public override void OnCancel() + { + cancelled = true; + base.OnCancel(); + } + + /// + /// This is called automatically when the Edit button is pressed. + /// (in Doom Builder 1, this was always the right mousebutton) + /// + [BeginAction("classicedit", BaseAction = true)] + protected virtual void OnEditBegin() + { + } + + /// + /// This is called automatically when the Edit button is released. + /// (in Doom Builder 1, this was always the right mousebutton) + /// + [EndAction("classicedit", BaseAction = true)] + protected virtual void OnEditEnd() + { + } + + /// + /// This is called automatically when the Select button is pressed. + /// (in Doom Builder 1, this was always the left mousebutton) + /// + [BeginAction("classicselect", BaseAction = true)] + protected virtual void OnSelectBegin() + { + } + + /// + /// This is called automatically when the Select button is released. + /// (in Doom Builder 1, this was always the left mousebutton) + /// + [EndAction("classicselect", BaseAction = true)] + protected virtual void OnSelectEnd() + { + if (selecting) OnEndMultiSelection(); + } + + /// + /// This is called automatically when a rectangular multi-selection ends. + /// + protected virtual void OnEndMultiSelection() + { + selecting = false; + } + + /// + /// Call this to initiate a rectangular multi-selection. + /// + protected virtual void StartMultiSelection() + { + selecting = true; + selectstart = mousemappos; + selectionrect = new RectangleF(selectstart.x, selectstart.y, 0, 0); + } + + /// + /// This is called automatically when a multi-selection is updated. + /// + protected virtual void OnUpdateMultiSelection() + { + selectionrect.X = selectstart.x; + selectionrect.Y = selectstart.y; + selectionrect.Width = mousemappos.x - selectstart.x; + selectionrect.Height = mousemappos.y - selectstart.y; + + if (selectionrect.Width < 0f) + { + selectionrect.Width = -selectionrect.Width; + selectionrect.X -= selectionrect.Width; + } + + if (selectionrect.Height < 0f) + { + selectionrect.Height = -selectionrect.Height; + selectionrect.Y -= selectionrect.Height; + } + } + + /// + /// Call this to draw the selection on the overlay layer. + /// Must call renderer.StartOverlay first! + /// + protected virtual void RenderMultiSelection() + { + renderer.RenderRectangle(selectionrect, SELECTION_BORDER_SIZE, + General.Colors.Highlight.WithAlpha(SELECTION_ALPHA), true); + } + + /// + /// This is called automatically when the mouse is moved while panning + /// + protected virtual void OnUpdateViewPanning() + { + // We can only drag the map when the mouse pointer is inside + // otherwise we don't have coordinates where to drag the map to + if (mouseinside && !float.IsNaN(mouselastpos.x) && !float.IsNaN(mouselastpos.y)) + { + // Get the map coordinates of the last mouse posision (before it moved) + Vector2D lastmappos = renderer2d.DisplayToMap(mouselastpos); + + // Do the scroll + ScrollBy(lastmappos.x - mousemappos.x, lastmappos.y - mousemappos.y); + } + } + + #endregion + + #region ================== Actions + + [BeginAction("gridsetup", BaseAction = true)] + protected void ShowGridSetup() + { + General.Map.Grid.ShowGridSetup(); + } + + [BeginAction("pan_view", BaseAction = true)] + protected virtual void BeginViewPan() + { + panning = true; + } + + [EndAction("pan_view", BaseAction = true)] + protected virtual void EndViewPan() + { + panning = false; + } + + [BeginAction("viewmodenormal", BaseAction = true)] + protected virtual void ViewModeNormal() + { + SetViewMode(ViewMode.Normal); + } + + // villsa + [BeginAction("viewmodefloorcolor", BaseAction = true)] + protected virtual void ViewModeFloorColor() + { + SetViewMode(ViewMode.FloorColor); + + foreach (Sector s in General.Map.Map.Sectors) + s.UpdateNeeded = true; + + General.Map.Map.Update(); + General.MainWindow.RedrawDisplay(); + } + + // villsa + [BeginAction("viewmodeceilingcolor", BaseAction = true)] + protected virtual void ViewModeCeilingColor() + { + SetViewMode(ViewMode.CeilingColor); + + foreach (Sector s in General.Map.Map.Sectors) + s.UpdateNeeded = true; + + General.Map.Map.Update(); + General.MainWindow.RedrawDisplay(); + } + + // villsa + [BeginAction("viewmodethingcolor", BaseAction = true)] + protected virtual void ViewModeThingColor() + { + SetViewMode(ViewMode.ThingColor); + + foreach (Sector s in General.Map.Map.Sectors) + s.UpdateNeeded = true; + + General.Map.Map.Update(); + General.MainWindow.RedrawDisplay(); + } + + [BeginAction("viewmodefloors", BaseAction = true)] + protected virtual void ViewModeFloors() + { + SetViewMode(ViewMode.FloorTextures); + + // villsa + if (General.Map.FormatInterface.InDoom64Mode) + { + foreach (Sector s in General.Map.Map.Sectors) + s.UpdateNeeded = true; + + General.Map.Map.Update(); + General.MainWindow.RedrawDisplay(); + } + } + + [BeginAction("viewmodeceilings", BaseAction = true)] + protected virtual void ViewModeCeilings() + { + SetViewMode(ViewMode.CeilingTextures); + + // villsa + if (General.Map.FormatInterface.InDoom64Mode) + { + foreach (Sector s in General.Map.Map.Sectors) + s.UpdateNeeded = true; + + General.Map.Map.Update(); + General.MainWindow.RedrawDisplay(); + } + } + + #endregion + } +} diff --git a/Source/Core/Editing/CopyPasteManager.cs b/Source/Core/Editing/CopyPasteManager.cs new file mode 100644 index 0000000..1e10090 --- /dev/null +++ b/Source/Core/Editing/CopyPasteManager.cs @@ -0,0 +1,536 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using CodeImp.DoomBuilder.Windows; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Rendering; +using System.Diagnostics; +using CodeImp.DoomBuilder.Actions; +using ICSharpCode.SharpZipLib.BZip2; +using CodeImp.DoomBuilder.Config; +using CodeImp.DoomBuilder.Geometry; + +#endregion + +namespace CodeImp.DoomBuilder.Editing +{ + public class CopyPasteManager + { + #region ================== Constants + + private const string CLIPBOARD_DATA_FORMAT = "DOOM_BUILDER_GEOMETRY"; + + #endregion + + #region ================== Variables + + // Disposing + private bool isdisposed = false; + + // Last inserted prefab + private string lastprefabfile; + + #endregion + + #region ================== Properties + + public bool IsDisposed { get { return isdisposed; } } + public bool IsPreviousPrefabAvailable { get { return (lastprefabfile != null); } } + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + internal CopyPasteManager() + { + // Initialize + + // Bind any methods + General.Actions.BindMethods(this); + + // We have no destructor + GC.SuppressFinalize(this); + } + + // Disposer + internal void Dispose() + { + // Not already disposed? + if (!isdisposed) + { + // Unbind any methods + General.Actions.UnbindMethods(this); + + // Done + isdisposed = true; + } + } + + #endregion + + #region ================== Methods + + // This makes a prefab of the selection. Returns null when cancelled. + internal MemoryStream MakePrefab() + { + // Let the plugins know + if (General.Plugins.OnCopyBegin()) + { + // Ask the editing mode to prepare selection for copying. + // The edit mode should mark all vertices, lines and sectors + // that need to be copied. + if (General.Editing.Mode.OnCopyBegin()) + { + // Copy the marked geometry + // This links sidedefs that are not linked to a marked sector to a virtual sector + MapSet copyset = General.Map.Map.CloneMarked(); + + // Convert flags and activations to UDMF fields, if needed + if (!(General.Map.FormatInterface is UniversalMapSetIO)) copyset.TranslateToUDMF(); + + // Write data to stream + MemoryStream memstream = new MemoryStream(); + UniversalStreamWriter writer = new UniversalStreamWriter(); + writer.RememberCustomTypes = false; + writer.Write(copyset, memstream, null); + + // Compress the stream + MemoryStream compressed = new MemoryStream((int)memstream.Length); + memstream.Seek(0, SeekOrigin.Begin); + BZip2.Compress(memstream, compressed, 900000); + + // Done + memstream.Dispose(); + General.Editing.Mode.OnCopyEnd(); + General.Plugins.OnCopyEnd(); + return compressed; + } + } + + // Aborted + return null; + } + + // This pastes a prefab. Returns false when paste was cancelled. + public void InsertPrefabStream(Stream stream, PasteOptions options) + { + // Cancel volatile mode + General.Editing.DisengageVolatileMode(); + + // Let the plugins know + if (General.Plugins.OnPasteBegin(options)) + { + // Ask the editing mode to prepare selection for pasting. + if (General.Editing.Mode.OnPasteBegin(options)) + { + Cursor oldcursor = Cursor.Current; + Cursor.Current = Cursors.WaitCursor; + + if (stream != null) + PastePrefab(stream, options); + + General.MainWindow.UpdateInterface(); + + Cursor.Current = oldcursor; + } + } + } + + // This pastes a prefab. Returns false when paste was cancelled. + internal void PastePrefab(Stream filedata, PasteOptions options) + { + // Create undo + General.MainWindow.DisplayStatus(StatusType.Action, "Inserted prefab."); + General.Map.UndoRedo.CreateUndo("Insert prefab"); + + // Decompress stream + MemoryStream decompressed = new MemoryStream((int)filedata.Length * 3); + filedata.Seek(0, SeekOrigin.Begin); + BZip2.Decompress(filedata, decompressed); + MemoryStream memstream = new MemoryStream(decompressed.ToArray()); + decompressed.Dispose(); + + // Mark all current geometry + General.Map.Map.ClearAllMarks(true); + + // Read data stream + UniversalStreamReader reader = new UniversalStreamReader(); + reader.StrictChecking = false; + General.Map.Map.BeginAddRemove(); + reader.Read(General.Map.Map, memstream); + General.Map.Map.EndAddRemove(); + + // The new geometry is not marked, so invert the marks to get it marked + General.Map.Map.InvertAllMarks(); + + // Convert UDMF fields back to flags and activations, if needed + if (!(General.Map.FormatInterface is UniversalMapSetIO)) General.Map.Map.TranslateFromUDMF(); + + // Modify tags and actions if preferred + if (options.ChangeTags == PasteOptions.TAGS_REMOVE) Tools.RemoveMarkedTags(); + if (options.ChangeTags == PasteOptions.TAGS_RENUMBER) Tools.RenumberMarkedTags(); + if (options.RemoveActions) Tools.RemoveMarkedActions(); + + // Done + memstream.Dispose(); + General.Map.Map.UpdateConfiguration(); + General.Map.ThingsFilter.Update(); + General.Editing.Mode.OnPasteEnd(options); + General.Plugins.OnPasteEnd(options); + } + + // This performs the copy. Returns false when copy was cancelled. + private bool DoCopySelection(string desc) + { + // Let the plugins know + if (General.Plugins.OnCopyBegin()) + { + // Ask the editing mode to prepare selection for copying. + // The edit mode should mark all vertices, lines and sectors + // that need to be copied. + if (General.Editing.Mode.OnCopyBegin()) + { + General.MainWindow.DisplayStatus(StatusType.Action, desc); + + // Copy the marked geometry + // This links sidedefs that are not linked to a marked sector to a virtual sector + MapSet copyset = General.Map.Map.CloneMarked(); + + // Convert flags and activations to UDMF fields, if needed + if (!(General.Map.FormatInterface is UniversalMapSetIO)) copyset.TranslateToUDMF(); + + // Write data to stream + MemoryStream memstream = new MemoryStream(); + UniversalStreamWriter writer = new UniversalStreamWriter(); + writer.RememberCustomTypes = false; + writer.Write(copyset, memstream, null); + + // Set on clipboard + Clipboard.SetData(CLIPBOARD_DATA_FORMAT, memstream); + + // Done + memstream.Dispose(); + General.Editing.Mode.OnCopyEnd(); + General.Plugins.OnCopyEnd(); + return true; + } + } + + // Aborted + return false; + } + + // This performs the paste. Returns false when paste was cancelled. + private bool DoPasteSelection(PasteOptions options) + { + // Anything to paste? + if (Clipboard.ContainsData(CLIPBOARD_DATA_FORMAT)) + { + // Cancel volatile mode + General.Editing.DisengageVolatileMode(); + + // Let the plugins know + if (General.Plugins.OnPasteBegin(options)) + { + // Ask the editing mode to prepare selection for pasting. + if (General.Editing.Mode.OnPasteBegin(options.Copy())) + { + // Create undo + General.MainWindow.DisplayStatus(StatusType.Action, "Pasted selected elements."); + General.Map.UndoRedo.CreateUndo("Paste"); + + // Read from clipboard + Stream memstream = (Stream)Clipboard.GetData(CLIPBOARD_DATA_FORMAT); + memstream.Seek(0, SeekOrigin.Begin); + + // Mark all current geometry + General.Map.Map.ClearAllMarks(true); + + // Read data stream + UniversalStreamReader reader = new UniversalStreamReader(); + reader.StrictChecking = false; + General.Map.Map.BeginAddRemove(); + reader.Read(General.Map.Map, memstream); + General.Map.Map.EndAddRemove(); + + // The new geometry is not marked, so invert the marks to get it marked + General.Map.Map.InvertAllMarks(); + + // Convert UDMF fields back to flags and activations, if needed + if (!(General.Map.FormatInterface is UniversalMapSetIO)) General.Map.Map.TranslateFromUDMF(); + + // Modify tags and actions if preferred + if (options.ChangeTags == PasteOptions.TAGS_REMOVE) Tools.RemoveMarkedTags(); + if (options.ChangeTags == PasteOptions.TAGS_RENUMBER) Tools.RenumberMarkedTags(); + if (options.RemoveActions) Tools.RemoveMarkedActions(); + + // Clean up + memstream.Dispose(); + + // Check if anything was pasted + int totalpasted = General.Map.Map.GetMarkedThings(true).Count; + totalpasted += General.Map.Map.GetMarkedVertices(true).Count; + totalpasted += General.Map.Map.GetMarkedLinedefs(true).Count; + totalpasted += General.Map.Map.GetMarkedSidedefs(true).Count; + totalpasted += General.Map.Map.GetMarkedSectors(true).Count; + if (totalpasted > 0) + { + General.Map.Map.UpdateConfiguration(); + General.Map.ThingsFilter.Update(); + General.Editing.Mode.OnPasteEnd(options.Copy()); + General.Plugins.OnPasteEnd(options); + } + return true; + } + } + + // Aborted + return false; + } + else + { + // Nothing usefull on the clipboard + General.MessageBeep(MessageBeepType.Warning); + return false; + } + } + + #endregion + + #region ================== Actions + + // This copies the current selection + [BeginAction("copyselection")] + public void CopySelection() + { + DoCopySelection("Copied selected elements."); + } + + // This cuts the current selection + [BeginAction("cutselection")] + public void CutSelection() + { + // Copy selected geometry + if (DoCopySelection("Cut selected elements.")) + { + // Get the delete action and check if it's bound + Actions.Action deleteitem = General.Actions["builder_deleteitem"]; + if (deleteitem.BeginBound) + { + // Perform delete action + deleteitem.Begin(); + deleteitem.End(); + } + else + { + // Action not bound + General.Interface.DisplayStatus(StatusType.Warning, "Cannot remove that in this mode."); + } + } + } + + // This pastes what is on the clipboard and marks the new geometry + [BeginAction("pasteselectionspecial")] + public void PasteSelectionSpecial() + { + PasteOptionsForm form = new PasteOptionsForm(); + DialogResult result = form.ShowDialog(General.MainWindow); + if (result == DialogResult.OK) DoPasteSelection(form.Options); + form.Dispose(); + } + + // This pastes what is on the clipboard and marks the new geometry + [BeginAction("pasteselection")] + public void PasteSelection() + { + DoPasteSelection(General.Settings.PasteOptions); + } + + // This creates a new prefab from selection + [BeginAction("createprefab")] + public void CreatePrefab() + { + Cursor oldcursor = Cursor.Current; + Cursor.Current = Cursors.WaitCursor; + + MemoryStream data = MakePrefab(); + if (data != null) + { + Cursor.Current = oldcursor; + + SaveFileDialog savefile = new SaveFileDialog(); + savefile.Filter = "Doom Builder Prefabs (*.dbprefab)|*.dbprefab"; + savefile.Title = "Save Prefab As"; + savefile.AddExtension = true; + savefile.CheckPathExists = true; + savefile.OverwritePrompt = true; + savefile.ValidateNames = true; + if (savefile.ShowDialog(General.MainWindow) == DialogResult.OK) + { + try + { + Cursor.Current = Cursors.WaitCursor; + if (File.Exists(savefile.FileName)) File.Delete(savefile.FileName); + File.WriteAllBytes(savefile.FileName, data.ToArray()); + } + catch (Exception e) + { + Cursor.Current = oldcursor; + General.ErrorLogger.Add(ErrorType.Error, e.GetType().Name + " while writing prefab to file: " + e.Message); + General.WriteLog(e.StackTrace); + General.ShowErrorMessage("Error while writing prefab to file! See log file for error details.", MessageBoxButtons.OK); + } + } + data.Dispose(); + } + else + { + // Can't make a prefab right now + General.MessageBeep(MessageBeepType.Warning); + } + + // Done + General.MainWindow.UpdateInterface(); + Cursor.Current = oldcursor; + } + + // This pastes a prefab from file + [BeginAction("insertprefabfile")] + public void InsertPrefabFile() + { + PasteOptions options = General.Settings.PasteOptions.Copy(); + + // Cancel volatile mode + General.Editing.DisengageVolatileMode(); + + // Let the plugins know + if (General.Plugins.OnPasteBegin(options)) + { + // Ask the editing mode to prepare selection for pasting. + if (General.Editing.Mode.OnPasteBegin(options)) + { + Cursor oldcursor = Cursor.Current; + + OpenFileDialog openfile = new OpenFileDialog(); + openfile.Filter = "Doom Builder Prefabs (*.dbprefab)|*.dbprefab"; + openfile.Title = "Open Prefab"; + openfile.AddExtension = false; + openfile.CheckFileExists = true; + openfile.Multiselect = false; + openfile.ValidateNames = true; + if (openfile.ShowDialog(General.MainWindow) == DialogResult.OK) + { + FileStream stream = null; + + try + { + Cursor.Current = Cursors.WaitCursor; + stream = File.OpenRead(openfile.FileName); + } + catch (Exception e) + { + Cursor.Current = oldcursor; + General.ErrorLogger.Add(ErrorType.Error, e.GetType().Name + " while reading prefab from file: " + e.Message); + General.WriteLog(e.StackTrace); + General.ShowErrorMessage("Error while reading prefab from file! See log file for error details.", MessageBoxButtons.OK); + } + + if (stream != null) + { + PastePrefab(stream, options); + lastprefabfile = openfile.FileName; + } + General.MainWindow.UpdateInterface(); + stream.Dispose(); + } + + Cursor.Current = oldcursor; + } + } + } + + // This pastes the previously inserted prefab + [BeginAction("insertpreviousprefab")] + public void InsertPreviousPrefab() + { + PasteOptions options = General.Settings.PasteOptions.Copy(); + + // Is there a previously inserted prefab? + if (IsPreviousPrefabAvailable) + { + // Does the file still exist? + if (File.Exists(lastprefabfile)) + { + // Cancel volatile mode + General.Editing.DisengageVolatileMode(); + + // Let the plugins know + if (General.Plugins.OnPasteBegin(options)) + { + // Ask the editing mode to prepare selection for pasting. + if (General.Editing.Mode.OnPasteBegin(options)) + { + Cursor oldcursor = Cursor.Current; + FileStream stream = null; + + try + { + Cursor.Current = Cursors.WaitCursor; + stream = File.OpenRead(lastprefabfile); + } + catch (Exception e) + { + Cursor.Current = oldcursor; + General.ErrorLogger.Add(ErrorType.Error, e.GetType().Name + " while reading prefab from file: " + e.Message); + General.WriteLog(e.StackTrace); + General.ShowErrorMessage("Error while reading prefab from file! See log file for error details.", MessageBoxButtons.OK); + } + + if (stream != null) PastePrefab(stream, options); + stream.Dispose(); + General.MainWindow.UpdateInterface(); + Cursor.Current = oldcursor; + } + } + } + else + { + General.MessageBeep(MessageBeepType.Warning); + lastprefabfile = null; + General.MainWindow.UpdateInterface(); + } + } + else + { + General.MessageBeep(MessageBeepType.Warning); + } + } + + #endregion + } +} diff --git a/Source/Core/Editing/CustomThingsFilter.cs b/Source/Core/Editing/CustomThingsFilter.cs new file mode 100644 index 0000000..873642f --- /dev/null +++ b/Source/Core/Editing/CustomThingsFilter.cs @@ -0,0 +1,79 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Config; + +#endregion + +namespace CodeImp.DoomBuilder.Editing +{ + public class CustomThingsFilter : ThingsFilter + { + #region ================== Variables + + #endregion + + #region ================== Properties + + public string Name { get { return name; } set { name = value; } } + public string CategoryName { get { return categoryname; } set { categoryname = value; } } + public int ThingType { get { return thingtype; } set { thingtype = value; } } + public ICollection RequiredFields { get { return requiredfields; } } + public ICollection ForbiddenFields { get { return forbiddenfields; } } + + #endregion + + #region ================== Constructor / Disposer + + // Constructor for a new filter + public CustomThingsFilter() + { + // Initialize + requiredfields = new List(); + forbiddenfields = new List(); + categoryname = ""; + thingtype = -1; + name = "Unnamed filter"; + + // We have no destructor + GC.SuppressFinalize(this); + } + + // Disposer + public virtual void Dispose() + { + base.Dispose(); + } + + #endregion + + #region ================== Methods + + #endregion + } +} diff --git a/Source/Core/Editing/EditMode.cs b/Source/Core/Editing/EditMode.cs new file mode 100644 index 0000000..a7abddb --- /dev/null +++ b/Source/Core/Editing/EditMode.cs @@ -0,0 +1,221 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using CodeImp.DoomBuilder.Windows; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Rendering; +using System.Diagnostics; +using CodeImp.DoomBuilder.Actions; +using CodeImp.DoomBuilder.Geometry; +using CodeImp.DoomBuilder.Config; + +#endregion + +namespace CodeImp.DoomBuilder.Editing +{ + /// + /// Provides basic user input interface functionality for a Doom Builder editing mode. + /// + public abstract class EditMode + { + #region ================== Constants + + public const int DRAG_START_MOVE_PIXELS = 5; + + #endregion + + #region ================== Variables + + // Attributes + private EditModeAttribute attributes; + + // Disposing + protected bool isdisposed = false; + + #endregion + + #region ================== Properties + + public bool IsDisposed { get { return isdisposed; } } + + public EditModeAttribute Attributes { get { return attributes; } } + + // Unless overriden, this returns the name of this mode + // for checking the appropriate button on the toolbar. + public virtual string EditModeButtonName { get { return GetType().Name; } } + + // Override this to provide a highlighted object, if applicable + public virtual object HighlightedObject { get { return null; } } + + #endregion + + #region ================== Constructor / Disposer + + /// + /// Provides basic user input interface functionality for a Doom Builder editing mode. + /// + public EditMode() + { + // Fetch attributes + object[] attrs = this.GetType().GetCustomAttributes(true); + foreach (object a in attrs) + { + if (a is EditModeAttribute) + { + attributes = (EditModeAttribute)a; + break; + } + } + + // No attributes found? + if (attributes == null) throw new Exception("Editing mode \"" + this.GetType().Name + "\" is missing EditMode attributes!"); + + // We have no destructor + GC.SuppressFinalize(this); + } + + // Disposer + public virtual void Dispose() + { + // Not already disposed? + if (!isdisposed) + { + // Clean up + + // Done + isdisposed = true; + } + } + + #endregion + + #region ================== Static Methods + + // This creates an instance of a specific mode + public static EditMode Create(Type modetype, object[] args) + { + try + { + // Create new mode + return (EditMode)General.ThisAssembly.CreateInstance(modetype.FullName, false, + BindingFlags.Default, null, args, CultureInfo.CurrentCulture, new object[0]); + } + // Catch errors + catch (TargetInvocationException e) + { + // Throw the actual exception + Debug.WriteLine(DateTime.Now.ToShortDateString() + " " + DateTime.Now.ToShortTimeString()); + Debug.WriteLine(e.InnerException.Source + " throws " + e.InnerException.GetType().Name + ":"); + Debug.WriteLine(e.InnerException.Message); + Debug.WriteLine(e.InnerException.StackTrace); + throw e.InnerException; + } + } + + #endregion + + #region ================== Events + + // + // Order in which events occur for the old and new modes: + // + // - Constructor of new mode is called + // - Disengage of old mode is called + // ----- Mode switches ----- + // - Engage of new mode is called + // - Dispose of old mode is called + // + + // Mode engages + public virtual void OnEngage() + { + // Bind any methods + General.Actions.BindMethods(this); + } + + // Mode disengages + public virtual void OnDisengage() + { + // Unbind any methods + General.Actions.UnbindMethods(this); + } + + // Called when the user presses F1 for Help + public virtual void OnHelp() { } + + // This forces the mode to cancel and return to the "parent" mode + public virtual void OnCancel() { } + public virtual void OnAccept() { } + + // Called before copying. Return false when copying should be cancelled. + // The edit mode should mark all vertices, lines and sectors + // that need to be copied. + public virtual bool OnCopyBegin() { return false; } + + // Called when the marked geometry has been copied. + public virtual void OnCopyEnd() { } + + // Called before pasting. Override this and return true to indicate that paste is allowed to contiue. + public virtual bool OnPasteBegin(PasteOptions options) { return false; } + + // Called after new geometry has been pasted in. The new geometry is marked. + public virtual void OnPasteEnd(PasteOptions options) { } + + // Called when undo/redo is used + // Return false to cancel undo action + public virtual bool OnUndoBegin() { return true; } + public virtual bool OnRedoBegin() { return true; } + public virtual void OnUndoEnd() { } + public virtual void OnRedoEnd() { } + + // Interface events + public virtual void OnMouseClick(MouseEventArgs e) { } + public virtual void OnMouseDoubleClick(MouseEventArgs e) { } + public virtual void OnMouseDown(MouseEventArgs e) { } + public virtual void OnMouseEnter(EventArgs e) { } + public virtual void OnMouseLeave(EventArgs e) { } + public virtual void OnMouseMove(MouseEventArgs e) { } + public virtual void OnMouseUp(MouseEventArgs e) { } + public virtual void OnKeyDown(KeyEventArgs e) { } + public virtual void OnKeyUp(KeyEventArgs e) { } + public virtual void OnMouseInput(Vector2D delta) { } + + // Rendering events + public virtual void OnRedrawDisplay() { } + public virtual void OnPresentDisplay() { } + + // Processing events + public virtual void OnProcess(double deltatime) { } + + // Generic events + public virtual void OnReloadResources() { } + public virtual void OnMapSetChangeBegin() { } + public virtual void OnMapSetChangeEnd() { } + + #endregion + } +} diff --git a/Source/Core/Editing/EditModeAttribute.cs b/Source/Core/Editing/EditModeAttribute.cs new file mode 100644 index 0000000..dc4f0d6 --- /dev/null +++ b/Source/Core/Editing/EditModeAttribute.cs @@ -0,0 +1,136 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Text; +using System.Windows.Forms; + +#endregion + +namespace CodeImp.DoomBuilder.Editing +{ + /// + /// This registers an EditMode derived class as a known editing mode within Doom Builder. + /// Allows automatic binding with an action and a button on the toolbar/menu. + /// + [AttributeUsage(AttributeTargets.Class, Inherited = true, AllowMultiple = true)] + public class EditModeAttribute : Attribute + { + #region ================== Variables + + // Properties + private string switchaction = null; + private string buttonimage = null; + private int buttonorder = 0; + private string buttongroup = "~none"; + private bool optional = true; + private bool isvolatile = false; + private string displayname = ""; + private bool allowcopypaste = true; + private bool usebydefault = false; + private bool safestartmode = false; + + #endregion + + #region ================== Properties + + /// + /// Sets the action name (as defined in the Actions.cfg resource) to + /// switch to this mode by using a shortcut key, toolbar button or menu item. + /// + public string SwitchAction { get { return switchaction; } set { switchaction = value; } } + + /// + /// Image resource name of the embedded resource that will be used for the + /// toolbar button and menu item. Leave this property out or set to null to + /// display no button for this mode. + /// + public string ButtonImage { get { return buttonimage; } set { buttonimage = value; } } + + /// + /// Sorting number for the order of buttons on the toolbar. Buttons with + /// lower values will be more to the left than buttons with higher values. + /// + public int ButtonOrder { get { return buttonorder; } set { buttonorder = value; } } + + /// + /// Grouping name for buttons on the toolbar. Groups are sorted alphabetically. + /// + public string ButtonGroup { get { return buttongroup; } set { buttongroup = value; } } + + /// + /// When set to false, this mode will always be available for use and the user cannot + /// change this in the game configuration. + /// + public bool Optional { get { return optional; } set { optional = value; } } + + /// + /// Set this to true to select this editing mode for use in all game configurations + /// by default. This only applies the first time and can still be changed by the user. + /// THIS OPTION MAY BE INTRUSIVE TO THE USER, USE WITH GREAT CARE! + /// + public bool UseByDefault { get { return usebydefault; } set { usebydefault = value; } } + + /// + /// When set to true, this mode is cancelled when core actions like + /// undo and save are performed. The editing mode should then return to + /// a non-volatile mode. + /// + public bool Volatile { get { return isvolatile; } set { isvolatile = value; } } + + /// + /// Name to display in the game configuration editing modes list and on the + /// information bar when the mode is currently active. + /// + public string DisplayName { get { return displayname; } set { displayname = value; } } + + /// + /// When set to false, the actions Cut, Copy and Paste cannot be used + /// in this mode. Default for this property is true. + /// + public bool AllowCopyPaste { get { return allowcopypaste; } set { allowcopypaste = value; } } + + /// + /// Set this to true when it is safe to have the editor start in this mode when + /// opening a map. The user can then select this as starting mode in the configuration. + /// + public bool SafeStartMode { get { return safestartmode; } set { safestartmode = value; } } + + #endregion + + #region ================== Constructor / Disposer + + /// + /// This registers an EditMode derived class as a known editing mode within Doom Builder. + /// Allows automatic binding with an action and a button on the toolbar/menu. + /// + public EditModeAttribute() + { + // Initialize + } + + #endregion + + #region ================== Methods + + #endregion + } +} diff --git a/Source/Core/Editing/EditModeInfo.cs b/Source/Core/Editing/EditModeInfo.cs new file mode 100644 index 0000000..61e8e76 --- /dev/null +++ b/Source/Core/Editing/EditModeInfo.cs @@ -0,0 +1,224 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using CodeImp.DoomBuilder.Actions; +using CodeImp.DoomBuilder.Plugins; +using System.Drawing; +using CodeImp.DoomBuilder.VisualModes; + +#endregion + +namespace CodeImp.DoomBuilder.Editing +{ + internal class EditModeInfo : IComparable + { + #region ================== Constants + + #endregion + + #region ================== Variables + + // Mode type + private Plugin plugin; + private Type type; + private EditModeAttribute attribs; + + // Mode switching + private BeginActionAttribute switchactionattr = null; + private ActionDelegate switchactiondel = null; + + // Mode button + private Stream buttonimagestream = null; + private Image buttonimage = null; + private string buttondesc = null; + private int buttonorder = int.MaxValue; + + #endregion + + #region ================== Properties + + public Plugin Plugin { get { return plugin; } } + public Type Type { get { return type; } } + public bool IsOptional { get { return ((switchactionattr != null) || (buttonimage != null)) && attribs.Optional; } } + public BeginActionAttribute SwitchAction { get { return switchactionattr; } } + public Image ButtonImage { get { return buttonimage; } } + public string ButtonDesc { get { return buttondesc; } } + public EditModeAttribute Attributes { get { return attribs; } } + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + public EditModeInfo(Plugin plugin, Type type, EditModeAttribute attr) + { + // Initialize + this.plugin = plugin; + this.type = type; + this.attribs = attr; + + // Make switch action info + if ((attribs.SwitchAction != null) && (attribs.SwitchAction.Length > 0)) + switchactionattr = new BeginActionAttribute(attribs.SwitchAction); + + // Make button info + if (attr.ButtonImage != null) + { + buttonimagestream = plugin.GetResourceStream(attr.ButtonImage); + if (buttonimagestream != null) + { + buttonimage = Image.FromStream(buttonimagestream); + buttondesc = attr.DisplayName; + buttonorder = attr.ButtonOrder; + } + } + + // We have no destructor + GC.SuppressFinalize(this); + } + + // Disposer + public void Dispose() + { + // Dispose + UnbindSwitchAction(); + buttonimage.Dispose(); + buttonimagestream.Dispose(); + + // Clean up + plugin = null; + } + + #endregion + + #region ================== Methods + + // This binds the action to switch to this editing mode + public void BindSwitchAction() + { + if ((switchactiondel == null) && (switchactionattr != null)) + { + switchactiondel = new ActionDelegate(UserSwitchToMode); + General.Actions.BindBeginDelegate(plugin.Assembly, switchactiondel, switchactionattr); + } + } + + // This unbind the switch action + public void UnbindSwitchAction() + { + if (switchactiondel != null) + { + General.Actions.UnbindBeginDelegate(plugin.Assembly, switchactiondel, switchactionattr); + switchactiondel = null; + } + } + + // This switches to the mode by user command + // (when user presses shortcut key) + public void UserSwitchToMode() + { + EditMode newmode; + + // Only when a map is opened + if (General.Map != null) + { + // Switching from volatile mode to volatile mode? + if ((General.Editing.Mode != null) && General.Editing.Mode.Attributes.Volatile && this.attribs.Volatile) + { + // First cancel previous volatile mode + General.Editing.CancelVolatileMode(); + } + + // When in VisualMode and switching to the same VisualMode, then we switch back to the previous classic mode + if ((General.Editing.Mode is VisualMode) && (type == General.Editing.Mode.GetType())) + { + // Switch back to last classic mode + General.Editing.ChangeMode(General.Editing.PreviousClassicMode.Name); + } + else + { + // Create instance + newmode = plugin.CreateObject(type); + + // Switch mode + General.Editing.ChangeMode(newmode); + } + } + } + + // This switches to the mode + public void SwitchToMode() + { + EditMode newmode; + + // Only when a map is opened + if (General.Map != null) + { + // Create instance + newmode = plugin.CreateObject(type); + + // Switch mode + General.Editing.ChangeMode(newmode); + } + } + + // This switches to the mode with arguments + public void SwitchToMode(object[] args) + { + EditMode newmode; + + // Only when a map is opened + if (General.Map != null) + { + // Create instance + newmode = plugin.CreateObjectA(type, args); + + // Switch mode + if (!General.Editing.ChangeMode(newmode)) + { + // When cancelled, dispose mode + newmode.Dispose(); + } + } + } + + // String representation + public override string ToString() + { + return attribs.DisplayName; + } + + // Compare by button order + public int CompareTo(EditModeInfo other) + { + if (this.buttonorder > other.buttonorder) return 1; + else if (this.buttonorder < other.buttonorder) return -1; + else return 0; + } + + #endregion + } +} diff --git a/Source/Core/Editing/EditingManager.cs b/Source/Core/Editing/EditingManager.cs new file mode 100644 index 0000000..3ea58cb --- /dev/null +++ b/Source/Core/Editing/EditingManager.cs @@ -0,0 +1,445 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using CodeImp.DoomBuilder.Windows; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Rendering; +using System.Diagnostics; +using CodeImp.DoomBuilder.Actions; +using CodeImp.DoomBuilder.Geometry; +using CodeImp.DoomBuilder.Plugins; +using CodeImp.DoomBuilder.VisualModes; + +#endregion + +namespace CodeImp.DoomBuilder.Editing +{ + public sealed class EditingManager + { + #region ================== Constants + + #endregion + + #region ================== Variables + + // All editing mode groups, sorted alphabetically + private List groups; + + // All editing modes available + private List allmodes; + + // Editing modes selected through configuration + private List usedmodes; + + // Status + private EditMode mode; + private EditMode newmode; + private Type prevmode; + private Type prevstablemode; + private Type prevclassicmode; + private bool disengaging; + + // Disposing + private bool isdisposed = false; + + #endregion + + #region ================== Properties + + internal List ModesInfo { get { return allmodes; } } + public EditMode Mode { get { return mode; } } + public EditMode NewMode { get { return newmode; } } + public Type PreviousMode { get { return prevmode; } } + public Type PreviousStableMode { get { return prevstablemode; } } + public Type PreviousClassicMode { get { return prevclassicmode; } } + public bool IsDisposed { get { return isdisposed; } } + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + internal EditingManager() + { + // Initialize + allmodes = new List(); + usedmodes = new List(); + groups = new List(); + + // Bind any methods + General.Actions.BindMethods(this); + + // Make list of all editing modes we can find + foreach (Plugin p in General.Plugins.Plugins) + { + // For all classes that inherit from EditMode + Type[] editclasses = p.FindClasses(typeof(EditMode)); + foreach (Type t in editclasses) + { + // For all defined EditMode attributes + EditModeAttribute[] emattrs = (EditModeAttribute[])t.GetCustomAttributes(typeof(EditModeAttribute), false); + foreach (EditModeAttribute a in emattrs) + { + // Make edit mode information + EditModeInfo modeinfo = new EditModeInfo(p, t, a); + allmodes.Add(modeinfo); + + // Add group if not added yet + if (!groups.Contains(modeinfo.Attributes.ButtonGroup)) + groups.Add(modeinfo.Attributes.ButtonGroup); + } + } + } + + // Sort the lists + allmodes.Sort(); + groups.Sort(); + + // Update modes + UpdateCurrentEditModes(); + + // We have no destructor + GC.SuppressFinalize(this); + } + + // Disposer + internal void Dispose() + { + // Not already disposed? + if (!isdisposed) + { + // Unbind any methods + General.Actions.UnbindMethods(this); + + // Clean up + + // Done + isdisposed = true; + } + } + + #endregion + + #region ================== Switch Actions + + // This unbinds all editing mode switch actions + private void UnbindSwitchActions() + { + foreach (EditModeInfo emi in allmodes) + { + emi.UnbindSwitchAction(); + } + } + + // This binds all editing mode switch actions for the available modes only + private void BindAvailableSwitchActions() + { + // In case of VisualMode, we only bind the switch action + // of the VisualMode to switch back to the previous mode + if (mode is VisualMode) + { + // Bind only the switch action for this mode + EditModeInfo info = GetEditModeInfo(mode.GetType()); + info.BindSwitchAction(); + } + else + { + // Bind all available mode swtich actions + foreach (EditModeInfo emi in usedmodes) + { + emi.BindSwitchAction(); + } + } + } + + #endregion + + #region ================== Methods + + // This cancels a volatile mode, as if the user presses cancel + public bool CancelVolatileMode() + { + // Volatile mode? + if ((General.Map != null) & (mode != null) && mode.Attributes.Volatile && !disengaging) + { + // Cancel + disengaging = true; + mode.OnCancel(); + return true; + } + else + { + // Mode is not volatile + return false; + } + } + + // This disengages a volatile mode, leaving the choice to cancel or accept to the editing mode + public bool DisengageVolatileMode() + { + // Volatile mode? + if ((General.Map != null) && (mode != null) && mode.Attributes.Volatile && !disengaging) + { + // Change back to normal mode + disengaging = true; + ChangeMode(prevstablemode.Name); + return true; + } + else + { + // Mode is not volatile + return false; + } + } + + // This returns specific editing mode info by name + internal EditModeInfo GetEditModeInfo(string editmodename) + { + // Find the edit mode + foreach (EditModeInfo emi in usedmodes) + { + // Mode matches class name? + if (emi.Type.Name == editmodename) return emi; + } + + // No such mode found + return null; + } + + // This returns specific editing mode info by name + internal EditModeInfo GetEditModeInfo(Type modetype) + { + // Find the edit mode + foreach (EditModeInfo emi in usedmodes) + { + // Mode matches class name? + if (emi.Type == modetype) return emi; + } + + // No such mode found + return null; + } + + // This is called when the editing modes must update + internal void UpdateCurrentEditModes() + { + // Unbind editing mode switch actions + UnbindSwitchActions(); + + // Rebuild list of used modes + usedmodes.Clear(); + if (General.Map != null) + { + foreach (EditModeInfo emi in allmodes) + { + // Include the mode when it is listed and enabled + // Also include the mode when it is not optional + if ((General.Map.ConfigSettings.EditModes.ContainsKey(emi.Type.FullName) && + General.Map.ConfigSettings.EditModes[emi.Type.FullName]) || !emi.IsOptional) + { + // Add the mode to be used and bind switch action + usedmodes.Add(emi); + } + } + } + + // Bind switch action for used modes + BindAvailableSwitchActions(); + + // Remove editing mode buttons from interface + General.MainWindow.RemoveEditModeButtons(); + + // Go for all the editing mode groups + foreach (string grp in groups) + { + General.MainWindow.AddEditModeSeperator(); + + // Go for all used edit modes to add buttons + foreach (EditModeInfo emi in usedmodes) + { + if ((emi.ButtonImage != null) && (emi.ButtonDesc != null) && + (emi.Attributes.ButtonGroup == grp)) + General.MainWindow.AddEditModeButton(emi); + } + } + } + + // + // This changes the editing mode. + // Order in which events occur for the old and new modes: + // + // - Constructor of new mode is called + // - Disengage of old mode is called + // ----- Mode switches ----- + // - Engage of new mode is called + // - Dispose of old mode is called + // + // Returns false when cancelled + public bool ChangeMode(EditMode nextmode) + { + EditMode oldmode = mode; + + if (nextmode != null) + { + // Verify that this mode is usable + bool allowuse = false; + foreach (EditModeInfo emi in usedmodes) + { + if (emi.Type.FullName == nextmode.GetType().FullName) + { + allowuse = true; + break; + } + } + + if (!allowuse) + { + General.Interface.MessageBeep(MessageBeepType.Error); + General.WriteLogLine("Attempt to switch to an invalid edit mode " + nextmode.GetType().Name + "!"); + return false; + } + else + { + General.WriteLogLine("Preparing to change editing mode to " + nextmode.GetType().Name + "..."); + } + } + else + { + General.WriteLogLine("Stopping editing mode..."); + } + + // Remember previous mode + newmode = nextmode; + if (mode != null) + { + prevmode = mode.GetType(); + if (!mode.Attributes.Volatile) + { + prevstablemode = prevmode; + if (mode is ClassicMode) prevclassicmode = prevmode; + } + } + else + { + prevmode = null; + prevstablemode = null; + prevclassicmode = null; + } + + // Let the plugins know beforehand and check if not cancelled + if (General.Plugins.ModeChanges(oldmode, newmode)) + { + // Disenagage old mode + disengaging = true; + if (oldmode != null) oldmode.OnDisengage(); + + // Reset cursor + General.Interface.SetCursor(Cursors.Default); + + // Apply new mode + General.WriteLogLine("Editing mode changes from " + TypeNameOrNull(oldmode) + " to " + TypeNameOrNull(nextmode)); + General.WriteLogLine("Previous stable mode is " + TypeNameOrNull(prevstablemode) + ", previous classic mode is " + TypeNameOrNull(prevclassicmode)); + mode = newmode; + disengaging = false; + + // Engage new mode + if (newmode != null) newmode.OnEngage(); + + // Bind new switch actions + UnbindSwitchActions(); + BindAvailableSwitchActions(); + + // Update the interface + General.MainWindow.EditModeChanged(); + + // Dispose old mode + if (oldmode != null) oldmode.Dispose(); + + // Done switching + General.WriteLogLine("Editing mode change complete."); + newmode = null; + + // Redraw the display + General.MainWindow.RedrawDisplay(); + return true; + } + else + { + // Cancelled + General.WriteLogLine("Editing mode change cancelled."); + return false; + } + } + + // This changes mode by class name and optionally with arguments + public void ChangeMode(string classname, params object[] args) + { + EditModeInfo emi = GetEditModeInfo(classname); + if (emi != null) emi.SwitchToMode(args); + } + + // This returns the type name as string + private string TypeNameOrNull(Type type) + { + return (type != null) ? type.Name : "NULL"; + } + + // This returns the type name as string + private string TypeNameOrNull(object obj) + { + return (obj != null) ? obj.GetType().Name : "NULL"; + } + + #endregion + + #region ================== Actions + + /// + /// This cancels the current mode. + /// + [BeginAction("cancelmode")] + public void CancelMode() + { + // Let the mode know + if (mode != null) + mode.OnCancel(); + } + + /// + /// This accepts the changes in the current mode. + /// + [BeginAction("acceptmode")] + public void AcceptMode() + { + // Let the mode know + if (mode != null) + mode.OnAccept(); + } + + #endregion + } +} diff --git a/Source/Core/Editing/GridSetup.cs b/Source/Core/Editing/GridSetup.cs new file mode 100644 index 0000000..9dfc13b --- /dev/null +++ b/Source/Core/Editing/GridSetup.cs @@ -0,0 +1,304 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using CodeImp.DoomBuilder.VisualModes; +using CodeImp.DoomBuilder.Windows; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Rendering; +using System.Diagnostics; +using CodeImp.DoomBuilder.Actions; +using CodeImp.DoomBuilder.Data; +using CodeImp.DoomBuilder.Geometry; + +#endregion + +namespace CodeImp.DoomBuilder.Editing +{ + public class GridSetup + { + #region ================== Constants + + private const int DEFAULT_GRID_SIZE = 32; + + public const int SOURCE_TEXTURES = 0; + public const int SOURCE_FLATS = 1; + public const int SOURCE_FILE = 2; + + #endregion + + #region ================== Variables + + // Grid + private int gridsize; + private float gridsizef; + private float gridsizefinv; + + // Background + private string background = ""; + private int backsource; + private ImageData backimage = new UnknownImage(null); + private int backoffsetx, backoffsety; + private float backscalex, backscaley; + + // Disposing + private bool isdisposed; + + #endregion + + #region ================== Properties + + public int GridSize { get { return gridsize; } } + public float GridSizeF { get { return gridsizef; } } + internal string BackgroundName { get { return background; } } + internal int BackgroundSource { get { return backsource; } } + internal ImageData Background { get { return backimage; } } + internal int BackgroundX { get { return backoffsetx; } } + internal int BackgroundY { get { return backoffsety; } } + internal float BackgroundScaleX { get { return backscalex; } } + internal float BackgroundScaleY { get { return backscaley; } } + internal bool Disposed { get { return isdisposed; } } + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + internal GridSetup() + { + // Initialize + SetGridSize(DEFAULT_GRID_SIZE); + backscalex = 1.0f; + backscaley = 1.0f; + + // Register actions + General.Actions.BindMethods(this); + + // We have no destructor + GC.SuppressFinalize(this); + } + + // Disposer + internal void Dispose() + { + if (!isdisposed) + { + // Dispose image if needed + if (backimage is FileImage) (backimage as FileImage).Dispose(); + + // Clean up + backimage = null; + + // Unregister actions + General.Actions.UnbindMethods(this); + + // Done + isdisposed = true; + } + } + + #endregion + + #region ================== Methods + + // Write settings to configuration + internal void WriteToConfig(Configuration cfg, string path) + { + // Write settings + cfg.WriteSetting(path + ".background", background); + cfg.WriteSetting(path + ".backsource", backsource); + cfg.WriteSetting(path + ".backoffsetx", backoffsetx); + cfg.WriteSetting(path + ".backoffsety", backoffsety); + cfg.WriteSetting(path + ".backscalex", (int)(backscalex * 100.0f)); + cfg.WriteSetting(path + ".backscaley", (int)(backscaley * 100.0f)); + cfg.WriteSetting(path + ".gridsize", gridsize); + } + + // Read settings from configuration + internal void ReadFromConfig(Configuration cfg, string path) + { + // Read settings + background = cfg.ReadSetting(path + ".background", ""); + backsource = cfg.ReadSetting(path + ".backsource", 0); + backoffsetx = cfg.ReadSetting(path + ".backoffsetx", 0); + backoffsety = cfg.ReadSetting(path + ".backoffsety", 0); + backscalex = (float)cfg.ReadSetting(path + ".backscalex", 100) / 100.0f; + backscaley = (float)cfg.ReadSetting(path + ".backscaley", 100) / 100.0f; + gridsize = cfg.ReadSetting(path + ".gridsize", DEFAULT_GRID_SIZE); + + // Setup + SetGridSize(gridsize); + LinkBackground(); + } + + // This sets the grid size + internal void SetGridSize(int size) + { + // Change grid + this.gridsize = size; + this.gridsizef = (float)gridsize; + this.gridsizefinv = 1f / gridsizef; + + // Update in main window + General.MainWindow.UpdateGrid(gridsize); + } + + // This sets the background + internal void SetBackground(string name, int source) + { + // Set background + if (name == null) name = ""; + this.backsource = source; + this.background = name; + + // Find this image + LinkBackground(); + } + + // This sets the background view + internal void SetBackgroundView(int offsetx, int offsety, float scalex, float scaley) + { + // Set background offset + this.backoffsetx = offsetx; + this.backoffsety = offsety; + this.backscalex = scalex; + this.backscaley = scaley; + } + + // This finds and links the background image + internal void LinkBackground() + { + // Dispose image if needed + if (backimage is FileImage) (backimage as FileImage).Dispose(); + + // Where to load background from? + switch (backsource) + { + case SOURCE_TEXTURES: + backimage = General.Map.Data.GetTextureImage(background); + break; + + case SOURCE_FLATS: + backimage = General.Map.Data.GetFlatImage(background); + break; + + case SOURCE_FILE: + backimage = new FileImage(background, background, false, 1.0f, 1.0f); + break; + } + + // Make sure it is loaded + backimage.LoadImage(); + backimage.CreateTexture(); + } + + // This returns the next higher coordinate + public float GetHigher(float offset) + { + return (float)Math.Round((offset + (gridsizef * 0.5f)) * gridsizefinv) * gridsizef; + } + + // This returns the next lower coordinate + public float GetLower(float offset) + { + return (float)Math.Round((offset - (gridsizef * 0.5f)) * gridsizefinv) * gridsizef; + } + + // This snaps to the nearest grid coordinate + public Vector2D SnappedToGrid(Vector2D v) + { + return GridSetup.SnappedToGrid(v, gridsizef, gridsizefinv); + } + + // This snaps to the nearest grid coordinate + public static Vector2D SnappedToGrid(Vector2D v, float gridsize, float gridsizeinv) + { + Vector2D sv = new Vector2D((float)Math.Round(v.x * gridsizeinv) * gridsize, + (float)Math.Round(v.y * gridsizeinv) * gridsize); + + if (sv.x < General.Map.Config.LeftBoundary) sv.x = General.Map.Config.LeftBoundary; + else if (sv.x > General.Map.Config.RightBoundary) sv.x = General.Map.Config.RightBoundary; + + if (sv.y > General.Map.Config.TopBoundary) sv.y = General.Map.Config.TopBoundary; + else if (sv.y < General.Map.Config.BottomBoundary) sv.y = General.Map.Config.BottomBoundary; + + return sv; + } + + #endregion + + #region ================== Actions + + // This shows the grid setup dialog + internal void ShowGridSetup() + { + // Show preferences dialog + GridSetupForm gridform = new GridSetupForm(); + if (gridform.ShowDialog(General.MainWindow) == DialogResult.OK) + { + // Redraw display + General.MainWindow.RedrawDisplay(); + } + + // Done + gridform.Dispose(); + } + + // This changes grid size + // Note: these were incorrectly swapped before, hence the wrong action name + [BeginAction("gridinc")] + internal void DecreaseGrid() + { + // Not lower than 1 + if (gridsize >= 2) + { + // Change grid + SetGridSize(gridsize >> 1); + + // Redraw display + General.MainWindow.RedrawDisplay(); + } + } + + // This changes grid size + // Note: these were incorrectly swapped before, hence the wrong action name + [BeginAction("griddec")] + internal void IncreaseGrid() + { + // Not higher than 1024 + if (gridsize <= 512) + { + // Change grid + SetGridSize(gridsize << 1); + + // Redraw display + General.MainWindow.RedrawDisplay(); + } + } + + #endregion + } +} diff --git a/Source/Core/Editing/NullThingsFilter.cs b/Source/Core/Editing/NullThingsFilter.cs new file mode 100644 index 0000000..3de319a --- /dev/null +++ b/Source/Core/Editing/NullThingsFilter.cs @@ -0,0 +1,72 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using CodeImp.DoomBuilder.Map; + +#endregion + +namespace CodeImp.DoomBuilder.Editing +{ + internal class NullThingsFilter : ThingsFilter + { + #region ================== Constructor / Disposer + + // Constructor + internal NullThingsFilter() + { + this.name = "(show all)"; + } + + // Disposer + internal override void Dispose() + { + // Not already disposed? + if (!isdisposed) + { + // Clean up + + // Done + base.Dispose(); + } + } + + #endregion + + #region ================== Methods + + // This updates the lists + public override void Update() + { + // Make lists + visiblethings = new List(General.Map.Map.Things); + hiddenthings = new List(0); + thingsvisiblestate = new Dictionary(General.Map.Map.Things.Count); + foreach (Thing t in visiblethings) thingsvisiblestate.Add(t, true); + } + + #endregion + } +} diff --git a/Source/Core/Editing/ThingsFilter.cs b/Source/Core/Editing/ThingsFilter.cs new file mode 100644 index 0000000..4eca164 --- /dev/null +++ b/Source/Core/Editing/ThingsFilter.cs @@ -0,0 +1,274 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Config; + +#endregion + +namespace CodeImp.DoomBuilder.Editing +{ + public class ThingsFilter + { + #region ================== Constants + + #endregion + + #region ================== Variables + + // Display name of this filter + protected string name; + + // Filter by category + protected string categoryname; + + // Filter by exact thing + protected int thingtype; + + // Filter by fields + protected List requiredfields; + protected List forbiddenfields; + + // List of things + protected List visiblethings; + protected List hiddenthings; + protected Dictionary thingsvisiblestate; + + // Disposing + protected bool isdisposed = false; + + #endregion + + #region ================== Properties + + public string Name { get { return name; } internal set { name = value; } } + public string CategoryName { get { return categoryname; } internal set { categoryname = value; } } + internal int ThingType { get { return thingtype; } set { thingtype = value; } } + internal ICollection RequiredFields { get { return requiredfields; } } + internal ICollection ForbiddenFields { get { return forbiddenfields; } } + public ICollection VisibleThings { get { return visiblethings; } } + public ICollection HiddenThings { get { return hiddenthings; } } + internal bool IsDisposed { get { return isdisposed; } } + + #endregion + + #region ================== Constructor / Disposer + + // Copy constructor + internal ThingsFilter(ThingsFilter f) + { + // Copy + name = f.name; + categoryname = f.categoryname; + thingtype = f.thingtype; + requiredfields = new List(f.requiredfields); + forbiddenfields = new List(f.forbiddenfields); + } + + // Constructor for filter from configuration + internal ThingsFilter(Configuration cfg, string path) + { + IDictionary fields; + + // Initialize + requiredfields = new List(); + forbiddenfields = new List(); + + // Read settings from config + name = cfg.ReadSetting(path + ".name", "Unnamed filter"); + categoryname = cfg.ReadSetting(path + ".category", ""); + thingtype = cfg.ReadSetting(path + ".type", -1); + + // Read flags + // key is string, value must be boolean which indicates if + // its a required field (true) or forbidden field (false). + fields = cfg.ReadSetting(path + ".fields", new Hashtable()); + foreach (DictionaryEntry de in fields) + { + // Add to the corresponding list + if ((bool)de.Value == true) + requiredfields.Add(de.Key.ToString()); + else + forbiddenfields.Add(de.Key.ToString()); + } + + // We have no destructor + GC.SuppressFinalize(this); + } + + // Constructor for a new filter + internal ThingsFilter() + { + // Initialize + requiredfields = new List(); + forbiddenfields = new List(); + categoryname = ""; + thingtype = -1; + name = "Unnamed filter"; + + // We have no destructor + GC.SuppressFinalize(this); + } + + // Disposer + internal virtual void Dispose() + { + // Not already disposed? + if (!isdisposed) + { + // Clean up + visiblethings = null; + hiddenthings = null; + thingsvisiblestate = null; + + // Done + isdisposed = true; + } + } + + #endregion + + #region ================== Methods + + /// + /// This checks if a thing is visible. Throws an exception when the specified Thing does not exist in the map (filter not updated?). + /// + public bool IsThingVisible(Thing t) + { + return thingsvisiblestate[t]; + } + + // This writes the filter to configuration + internal void WriteSettings(Configuration cfg, string path) + { + // Write settings to config + cfg.WriteSetting(path + ".name", name); + cfg.WriteSetting(path + ".category", categoryname); + cfg.WriteSetting(path + ".type", thingtype); + + // Write required fields to config + foreach (string s in requiredfields) + cfg.WriteSetting(path + ".fields." + s, true); + + // Write forbidden fields to config + foreach (string s in forbiddenfields) + cfg.WriteSetting(path + ".fields." + s, false); + } + + // This is called when the filter is activated + internal virtual void Activate() + { + // Update the list of things + Update(); + } + + // This is called when the filter is deactivates + internal virtual void Deactivate() + { + // Clear lists + visiblethings = null; + hiddenthings = null; + thingsvisiblestate = null; + } + + /// + /// This updates the list of things. + /// + public virtual void Update() + { + // Make new list + visiblethings = new List(General.Map.Map.Things.Count); + hiddenthings = new List(General.Map.Map.Things.Count); + thingsvisiblestate = new Dictionary(General.Map.Map.Things.Count); + foreach (Thing t in General.Map.Map.Things) + { + bool qualifies; + + // Get thing info + ThingTypeInfo ti = General.Map.Data.GetThingInfo(t.Type); + + // Check if thing is in unknown category + if (ti.Category == null) + { + // Check if the thing matches id + qualifies = ((t.Type == thingtype) || (thingtype == -1)) && (categoryname.Length == 0); + } + else + { + // Check if the thing matches category and id + qualifies = ((t.Type == thingtype) || (thingtype == -1)) && + ((ti.Category.Name == categoryname) || (categoryname.Length == 0)); + } + + // Still qualifies? + if (qualifies) + { + // Go for all required fields + foreach (string s in requiredfields) + { + if (t.Flags.ContainsKey(s)) + { + qualifies = (t.Flags[s] == true); + break; + } + else + { + qualifies = false; + break; + } + } + } + + // Still qualifies? + if (qualifies) + { + // Go for all forbidden fields + foreach (string s in forbiddenfields) + { + if (t.Flags.ContainsKey(s)) + { + qualifies = (t.Flags[s] == false); + break; + } + } + } + + // Put the thing in the lists + if (qualifies) visiblethings.Add(t); else hiddenthings.Add(t); + thingsvisiblestate.Add(t, qualifies); + } + } + + // String representation + public override string ToString() + { + return name; + } + + #endregion + } +} diff --git a/Source/Core/Editing/UndoManager.cs b/Source/Core/Editing/UndoManager.cs new file mode 100644 index 0000000..5dfe79b --- /dev/null +++ b/Source/Core/Editing/UndoManager.cs @@ -0,0 +1,1309 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Threading; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using CodeImp.DoomBuilder.Geometry; +using CodeImp.DoomBuilder.Plugins; +using CodeImp.DoomBuilder.Windows; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Rendering; +using System.Diagnostics; +using CodeImp.DoomBuilder.Actions; + +#endregion + +namespace CodeImp.DoomBuilder.Editing +{ + public class UndoManager + { + #region ================== Constants + + // Maximum undo/redo levels + private const int MAX_UNDO_LEVELS = 2000; + + // Default stream capacity + private const int STREAM_CAPACITY = 1000; + + // Stream codes + // "Prp" stands for property changes (uses the ReadWrite functions) + // "Ref" stands for reference changes + private enum StreamCodes : byte + { + AddVertex, + RemVertex, + PrpVertex, + AddLinedef, + RemLinedef, + PrpLinedef, + RefLinedefStart, + RefLinedefEnd, + RefLinedefFront, + RefLinedefBack, + AddSidedef, + RemSidedef, + PrpSidedef, + RefSidedefSector, + AddSector, + RemSector, + PrpSector, + AddThing, + RemThing, + PrpThing, + } + + #endregion + + #region ================== Variables + + // Undo and redo stacks + private List undos; + private List redos; + + // Grouping + private Plugin lastgroupplugin; + private int lastgroupid; + private int lastgrouptag; + + // Unique tickets + private int ticketid; + + // Writing stream + private UndoSnapshot snapshot; + private bool isundosnapshot; + private MemoryStream stream; + private SerializerStream ss; + private int commandswritten; + private long prevstreamlength; + private bool ignorepropchanges; + private bool isrecordingcommand; + private MapElement propsrecorded; + private bool geometrychanged; + private bool populationchanged; + + // Background thread + private volatile bool dobackgroundwork; + private Thread backgroundthread; + + // Disposing + private bool isdisposed = false; + + #endregion + + #region ================== Properties + + public UndoSnapshot NextUndo + { + get + { + if (!isundosnapshot && (snapshot != null)) + return snapshot; + else if (undos.Count > 0) + return undos[0]; + else + return null; + } + } + + public UndoSnapshot NextRedo + { + get + { + if (isundosnapshot && (snapshot != null)) + return snapshot; + else if (redos.Count > 0) + return redos[0]; + else + return null; + } + } + + public bool IsDisposed { get { return isdisposed; } } + + /// + /// This can be used to ignore insignificant element property changes. Any property changes + /// that are made while this is set to True will not be undoable. Use with great care! + /// + public bool IgnorePropChanges { get { return ignorepropchanges; } set { ignorepropchanges = value; } } + + /// + /// After undo or redo, this returns if the geometry changed. This includes add/remove operations + /// on sectors, linedefs, sidedefs and vertices, references changes and property changes on vertices. + /// + public bool GeometryChanged { get { return geometrychanged; } } + + /// + /// After undo or redo, this returns if things were added/removed. This does not include thing property changes. + /// + public bool PopulationChanged { get { return populationchanged; } } + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + internal UndoManager() + { + // Initialize + ticketid = 1; + undos = new List(MAX_UNDO_LEVELS + 1); + redos = new List(MAX_UNDO_LEVELS + 1); + + // Bind any methods + General.Actions.BindMethods(this); + + // Start background thread + backgroundthread = new Thread(new ThreadStart(BackgroundThread)); + backgroundthread.Name = "Snapshot Compressor"; + backgroundthread.Priority = ThreadPriority.Lowest; + backgroundthread.IsBackground = true; + backgroundthread.Start(); + + // We have no destructor + GC.SuppressFinalize(this); + } + + // Disposer + internal void Dispose() + { + // Not already disposed? + if (!isdisposed) + { + // Unbind any methods + General.Actions.UnbindMethods(this); + + // Stop the thread and wait for it to end + backgroundthread.Interrupt(); + backgroundthread.Join(); + backgroundthread = null; + + // Clean up + ClearUndos(); + ClearRedos(); + General.WriteLogLine("All undo and redo levels cleared."); + + // Done + isdisposed = true; + } + } + + #endregion + + #region ================== Private Methods + + // This clears the redos + private void ClearRedos() + { + lock (redos) + { + // Dispose all redos + foreach (UndoSnapshot u in redos) u.Dispose(); + redos.Clear(); + + // If the current snapshot is meant for redo, trash it also + if (isundosnapshot && (snapshot != null)) + { + FinishRecording(); + isundosnapshot = false; + snapshot = null; + } + } + } + + // This clears the undos + private void ClearUndos() + { + lock (undos) + { + // Dispose all undos + foreach (UndoSnapshot u in undos) u.Dispose(); + undos.Clear(); + + // If the current snapshot is meant for undo, trash it also + if (!isundosnapshot && (snapshot != null)) + { + FinishRecording(); + isundosnapshot = false; + snapshot = null; + } + } + } + + // This checks and removes a level when the limit is reached + private void LimitUndoRedoLevel(List list) + { + UndoSnapshot u; + + // Too many? + if (list.Count > MAX_UNDO_LEVELS) + { + // Remove one and dispose map + u = list[list.Count - 1]; + u.Dispose(); + list.RemoveAt(list.Count - 1); + } + } + + // Background thread + private void BackgroundThread() + { + while (true) + { + if (dobackgroundwork) + { + // First set dobackgroundwork to false before performing the work so + // that it can be set to true again when another pass is needed + dobackgroundwork = false; + + int undolevel = 0; + UndoSnapshot us; + while (true) + { + // Get the next snapshot or leave + lock (undos) + { + if (undolevel < undos.Count) + us = undos[undolevel]; + else + break; + } + + // Write to file or load from file, if needed + if (us.StoreOnDisk && !us.IsOnDisk) + us.WriteToFile(); + else if (!us.StoreOnDisk && us.IsOnDisk) + us.RestoreFromFile(); + + // Next + undolevel++; + } + + int redolevel = 0; + while (true) + { + // Get the next snapshot or leave + lock (redos) + { + if (redolevel < redos.Count) + us = redos[redolevel]; + else + break; + } + + // Write to file or load from file, if needed + if (us.StoreOnDisk && !us.IsOnDisk) + us.WriteToFile(); + else if (!us.StoreOnDisk && us.IsOnDisk) + us.RestoreFromFile(); + + // Next + redolevel++; + } + } + + try { Thread.Sleep(30); } + catch (ThreadInterruptedException) { break; } + } + } + + // This starts a new recording + private void StartRecording(string description) + { + stream = new MemoryStream(STREAM_CAPACITY); + ss = new SerializerStream(stream); + ss.Begin(); + commandswritten = 0; + propsrecorded = null; + snapshot = new UndoSnapshot(description, stream, ticketid); + } + + // This finishes recording + private void FinishRecording() + { + // End current recording + if ((stream != null) && (ss != null)) + { + propsrecorded = null; + ss.wInt(commandswritten); + ss.End(); + ss = null; + } + } + + // This begins writing to the record stream + private bool BeginRecordData(StreamCodes code) + { + if ((ss == null) || isdisposed) return false; + isrecordingcommand = true; + prevstreamlength = stream.Length; + ss.wByte((byte)code); + return true; + } + + // This ends writing to the record stream + private void EndRecordData() + { + // We write the difference in bytes to the stream so that + // the stream can be read from the end backwards + int delta = (int)(stream.Length - prevstreamlength); + ss.wInt(delta); + commandswritten++; + isrecordingcommand = false; + } + + // This outputs record info, if desired + private void LogRecordInfo(string info) + { +#if DEBUG + //General.WriteLogLine(info); +#endif + } + + // This plays back a stream in reverse + private void PlaybackStream(MemoryStream pstream) + { + General.Map.Map.AutoRemove = false; + + pstream.Seek(0, SeekOrigin.Begin); + DeserializerStream ds = new DeserializerStream(pstream); + ds.Begin(); + + if (pstream.Length > 4) + { + // Start at the end + pstream.Seek(ds.EndPosition - 4, SeekOrigin.Begin); + int numcmds; ds.rInt(out numcmds); + pstream.Seek(-8, SeekOrigin.Current); + while (numcmds > 0) + { + // Go back up the stream to the beginning of the prev command + int len; ds.rInt(out len); + pstream.Seek(-(len + 4), SeekOrigin.Current); + + // Play back the command + long beginpos = pstream.Position; + byte cmd; ds.rByte(out cmd); + switch ((StreamCodes)cmd) + { + case StreamCodes.AddVertex: PlayAddVertex(ds); break; + case StreamCodes.RemVertex: PlayRemVertex(ds); break; + case StreamCodes.PrpVertex: PlayPrpVertex(ds); break; + case StreamCodes.AddLinedef: PlayAddLinedef(ds); break; + case StreamCodes.RemLinedef: PlayRemLinedef(ds); break; + case StreamCodes.PrpLinedef: PlayPrpLinedef(ds); break; + case StreamCodes.RefLinedefStart: PlayRefLinedefStart(ds); break; + case StreamCodes.RefLinedefEnd: PlayRefLinedefEnd(ds); break; + case StreamCodes.RefLinedefFront: PlayRefLinedefFront(ds); break; + case StreamCodes.RefLinedefBack: PlayRefLinedefBack(ds); break; + case StreamCodes.AddSidedef: PlayAddSidedef(ds); break; + case StreamCodes.RemSidedef: PlayRemSidedef(ds); break; + case StreamCodes.PrpSidedef: PlayPrpSidedef(ds); break; + case StreamCodes.RefSidedefSector: PlayRefSidedefSector(ds); break; + case StreamCodes.AddSector: PlayAddSector(ds); break; + case StreamCodes.RemSector: PlayRemSector(ds); break; + case StreamCodes.PrpSector: PlayPrpSector(ds); break; + case StreamCodes.AddThing: PlayAddThing(ds); break; + case StreamCodes.RemThing: PlayRemThing(ds); break; + case StreamCodes.PrpThing: PlayPrpThing(ds); break; + } + + // Sanity check + if ((beginpos + len) != pstream.Position) + throw new Exception("The last command did not read the same amount of data that was written for this command!"); + + // Go back for next command + pstream.Seek(-(len + 4), SeekOrigin.Current); + + numcmds--; + } + } + + General.Map.Map.AutoRemove = true; + } + + #endregion + + #region ================== Public Methods + + // This makes a list of the undo levels in order they will be undone + public List GetUndoList() + { + List list = new List(undos.Count + 1); + if (!isundosnapshot && (snapshot != null)) + list.Add(snapshot); + list.AddRange(undos); + return list; + } + + // This makes a list of the redo levels in order they will be undone + public List GetRedoList() + { + List list = new List(redos.Count + 1); + if (isundosnapshot && (snapshot != null)) + list.Add(snapshot); + list.AddRange(redos); + return list; + } + + // This clears all redos + public void ClearAllRedos() + { + ClearRedos(); + General.MainWindow.UpdateInterface(); + } + + /// + /// This makes an undo and returns the unique ticket id. Also automatically indicates that the map is changed. + /// + /// Any description you want the undo to be named. Should be something related to the changes you are about to make. + /// Ticket ID that identifies the created undo level. + public int CreateUndo(string description) + { + return CreateUndo(description, null, 0, 0); + } + + /// + /// This makes an undo and returns the unique ticket id. Also automatically indicates that the map is changed. + /// + /// Any description you want the undo to be named. Should be something related to the changes you are about to make. + /// The object creating the undo. All objects from within the same plugin are equal, so it is safe to just use 'this' everywhere. This is only used for undo grouping and you can use 'null' if you don't want undo grouping. + /// The undo group id you want this undo level to group with (undos only group together if the previous undo has the same source, id and tag). Group 0 indicates no grouping. + /// The undo group tag you want this undo level to group with (undos only group together if the previous undo has the same source, id and tag). Use at your own discretion. + /// Ticket ID that identifies the created undo level. Returns -1 when no undo level was created. + public int CreateUndo(string description, object groupsource, int groupid, int grouptag) + { + UndoSnapshot u; + Plugin p = null; + string groupsourcename = "Null"; + + // Figure out the source plugin + if (groupsource != null) + { + p = General.Plugins.FindPluginByAssembly(groupsource.GetType().Assembly); + if (p != null) groupsourcename = p.Name; + } + + // Not the same as previous group, or no grouping desired... + if ((p == null) || (lastgroupplugin == null) || (p != lastgroupplugin) || + (groupid == 0) || (lastgroupid == 0) || (groupid != lastgroupid) || + (grouptag != lastgrouptag)) + { + FinishRecording(); + + // Next ticket id + if (++ticketid == int.MaxValue) ticketid = 1; + + General.WriteLogLine("Creating undo snapshot \"" + description + "\", Source " + groupsourcename + ", Group " + groupid + ", Tag " + grouptag + ", Ticket ID " + ticketid + "..."); + + if ((snapshot != null) && !isundosnapshot) + { + lock (undos) + { + // The current top of the stack can now be written to disk + // because it is no longer the next immediate undo level + if (undos.Count > 0) undos[0].StoreOnDisk = true; + + // Put it on the stack + undos.Insert(0, snapshot); + LimitUndoRedoLevel(undos); + } + } + + StartRecording(description); + isundosnapshot = false; + + // Clear all redos + ClearRedos(); + + // Keep grouping info + lastgroupplugin = p; + lastgroupid = groupid; + lastgrouptag = grouptag; + + // Map changes! + General.Map.IsChanged = true; + + // Update + dobackgroundwork = true; + General.Plugins.OnUndoCreated(); + General.MainWindow.UpdateInterface(); + + // Done + return ticketid; + } + else + { + return -1; + } + } + + // This removes a previously made undo + public void WithdrawUndo() + { + // Previously, withdrawing an undo level was possible, because each undo level contained + // an entire snapshot of the map. With the new progressive undo system, you cannot ignore + // any changes, so we have to actually perform the undo and trash the redo it creates. + PerformUndo(1, false); + ClearAllRedos(); + + General.Plugins.OnUndoWithdrawn(); + } + + // This performs an undo + [BeginAction("undo")] + public void PerformUndo() + { + PerformUndo(1); + } + + // This performs one or more undo levels + public void PerformUndo(int levels) { PerformUndo(levels, true); } + private void PerformUndo(int levels, bool showmessage) + { + UndoSnapshot u = null; + Cursor oldcursor = Cursor.Current; + Cursor.Current = Cursors.WaitCursor; + int levelsundone = 0; + + // Anything to undo? + if ((undos.Count > 0) || ((snapshot != null) && !isundosnapshot)) + { + // Let the plugins know + if (General.Plugins.OnUndoBegin()) + { + // Call UndoBegin event + if (General.Editing.Mode.OnUndoBegin()) + { + // Cancel volatile mode, if any + // This returns false when mode was not volatile + if (!General.Editing.CancelVolatileMode()) + { + geometrychanged = false; + populationchanged = false; + General.Map.Map.ClearAllMarks(false); + General.Map.Map.BeginAddRemove(); + + // Go for all levels to undo + for (int lvl = 0; lvl < levels; lvl++) + { + FinishRecording(); + + if (isundosnapshot) + { + if (snapshot != null) + { + // This snapshot was made by a previous call to this + // function and should go on the redo list + lock (redos) + { + // The current top of the stack can now be written to disk + // because it is no longer the next immediate redo level + if (redos.Count > 0) redos[0].StoreOnDisk = true; + + // Put it on the stack + redos.Insert(0, snapshot); + LimitUndoRedoLevel(redos); + } + } + } + else + { + // The snapshot can be undone immediately and it will + // be recorded for the redo list + if (snapshot != null) + u = snapshot; + } + + // No immediate snapshot to undo? Then get the next one from the stack + if (u == null) + { + lock (undos) + { + if (undos.Count > 0) + { + // Get undo snapshot + u = undos[0]; + undos.RemoveAt(0); + + // Make the current top of the stack load into memory + // because it just became the next immediate undo level + if (undos.Count > 0) undos[0].StoreOnDisk = false; + } + else + { + // Nothing more to undo + u = null; + break; + } + } + } + + General.WriteLogLine("Performing undo \"" + u.Description + "\", Ticket ID " + u.TicketID + "..."); + + if ((levels == 1) && showmessage) + General.Interface.DisplayStatus(StatusType.Action, u.Description + " undone."); + + // Make a snapshot for redo + StartRecording(u.Description); + isundosnapshot = true; + + // Reset grouping + lastgroupplugin = null; + + // Play back the stream in reverse + MemoryStream data = u.GetStream(); + PlaybackStream(data); + data.Dispose(); + + // Done with this snapshot + u = null; + levelsundone++; + } + + General.Map.Map.EndAddRemove(); + + if ((levels > 1) && showmessage) + General.Interface.DisplayStatus(StatusType.Action, "Undone " + levelsundone + " changes."); + + // Remove selection + General.Map.Map.ClearAllSelected(); + + // Update map + General.Map.Map.Update(); + foreach (Thing t in General.Map.Map.Things) if (t.Marked) t.UpdateConfiguration(); + General.Map.ThingsFilter.Update(); + General.Map.Data.UpdateUsedTextures(); + General.MainWindow.RefreshInfo(); + General.MainWindow.RedrawDisplay(); + + // Map changed! + General.Map.IsChanged = true; + + // Done + General.Editing.Mode.OnUndoEnd(); + General.Plugins.OnUndoEnd(); + + // Update interface + dobackgroundwork = true; + General.MainWindow.UpdateInterface(); + } + } + } + } + + Cursor.Current = oldcursor; + } + + // This performs a redo + [BeginAction("redo")] + public void PerformRedo() + { + PerformRedo(1); + } + + public void PerformRedo(int levels) + { + UndoSnapshot r = null; + Cursor oldcursor = Cursor.Current; + Cursor.Current = Cursors.WaitCursor; + int levelsundone = 0; + + // Anything to redo? + if ((redos.Count > 0) || ((snapshot != null) && isundosnapshot)) + { + // Let the plugins know + if (General.Plugins.OnRedoBegin()) + { + // Call RedoBegin event + if (General.Editing.Mode.OnRedoBegin()) + { + // Cancel volatile mode, if any + // This returns false when mode was not volatile + if (!General.Editing.CancelVolatileMode()) + { + geometrychanged = false; + populationchanged = false; + General.Map.Map.ClearAllMarks(false); + General.Map.Map.BeginAddRemove(); + + // Go for all levels to redo + for (int lvl = 0; lvl < levels; lvl++) + { + FinishRecording(); + + if (isundosnapshot) + { + // This snapshot was started by PerformUndo, which means + // it can directly be used to redo to previous undo + if (snapshot != null) + r = snapshot; + } + else + { + if (snapshot != null) + { + // This snapshot was made by a previous call to this + // function and should go on the undo list + lock (undos) + { + // The current top of the stack can now be written to disk + // because it is no longer the next immediate undo level + if (undos.Count > 0) undos[0].StoreOnDisk = true; + + // Put it on the stack + undos.Insert(0, snapshot); + LimitUndoRedoLevel(undos); + } + } + } + + // No immediate snapshot to redo? Then get the next one from the stack + if (r == null) + { + lock (redos) + { + if (redos.Count > 0) + { + // Get redo snapshot + r = redos[0]; + redos.RemoveAt(0); + + // Make the current top of the stack load into memory + // because it just became the next immediate undo level + if (redos.Count > 0) redos[0].StoreOnDisk = false; + } + else + { + // Nothing more to redo + r = null; + break; + } + } + } + + General.WriteLogLine("Performing redo \"" + r.Description + "\", Ticket ID " + r.TicketID + "..."); + + if (levels == 1) + General.Interface.DisplayStatus(StatusType.Action, r.Description + " redone."); + + StartRecording(r.Description); + isundosnapshot = false; + + // Reset grouping + lastgroupplugin = null; + + // Play back the stream in reverse + MemoryStream data = r.GetStream(); + PlaybackStream(data); + data.Dispose(); + + // Done with this snapshot + r = null; + levelsundone++; + } + + General.Map.Map.EndAddRemove(); + + if (levels > 1) + General.Interface.DisplayStatus(StatusType.Action, "Redone " + levelsundone + " changes."); + + // Remove selection + General.Map.Map.ClearAllSelected(); + + // Update map + General.Map.Map.Update(); + foreach (Thing t in General.Map.Map.Things) if (t.Marked) t.UpdateConfiguration(); + General.Map.ThingsFilter.Update(); + General.Map.Data.UpdateUsedTextures(); + General.MainWindow.RefreshInfo(); + General.MainWindow.RedrawDisplay(); + + // Map changed! + General.Map.IsChanged = true; + + // Done + General.Editing.Mode.OnRedoEnd(); + General.Plugins.OnRedoEnd(); + + // Update interface + dobackgroundwork = true; + General.MainWindow.UpdateInterface(); + } + } + } + } + + Cursor.Current = oldcursor; + } + + #endregion + + #region ================== Record and Playback + + internal void RecAddVertex(Vertex v) + { + if (!BeginRecordData(StreamCodes.AddVertex)) return; + ss.wInt(v.Index); + EndRecordData(); + + //LogRecordInfo("REC: Adding vertex " + v.Index + " at " + v.Position); + propsrecorded = null; + } + + internal void PlayAddVertex(DeserializerStream ds) + { + int index; ds.rInt(out index); + //LogRecordInfo("PLY: Removing vertex " + index); + Vertex v = General.Map.Map.GetVertexByIndex(index); + foreach (Linedef l in v.Linedefs) l.Marked = true; + v.Dispose(); + geometrychanged = true; + } + + internal void RecRemVertex(Vertex v) + { + if (!BeginRecordData(StreamCodes.RemVertex)) return; + ss.wInt(v.Index); + ss.wVector2D(v.Position); + v.ReadWrite(ss); + EndRecordData(); + + //LogRecordInfo("REC: Removing vertex " + v.Index + " (at " + v.Position + ")"); + propsrecorded = null; + } + + internal void PlayRemVertex(DeserializerStream ds) + { + int index; ds.rInt(out index); + Vector2D pos; ds.rVector2D(out pos); + //LogRecordInfo("PLY: Adding vertex " + index + " at " + pos); + Vertex v = General.Map.Map.CreateVertex(index, pos); + v.ReadWrite(ds); + v.Marked = true; + geometrychanged = true; + } + + internal void RecPrpVertex(Vertex v) + { + if (!ignorepropchanges && !isrecordingcommand && !object.ReferenceEquals(v, propsrecorded)) + { + if (!BeginRecordData(StreamCodes.PrpVertex)) return; + ss.wInt(v.Index); + v.ReadWrite(ss); + EndRecordData(); + propsrecorded = v; + } + } + + internal void PlayPrpVertex(DeserializerStream ds) + { + int index; ds.rInt(out index); + Vertex v = General.Map.Map.GetVertexByIndex(index); + v.ReadWrite(ds); + v.Marked = true; + geometrychanged = true; + } + + internal void RecAddLinedef(Linedef l) + { + if (!BeginRecordData(StreamCodes.AddLinedef)) return; + ss.wInt(l.Index); + EndRecordData(); + + //LogRecordInfo("REC: Adding linedef " + l.Index + " from " + ((l.Start != null) ? l.Start.Index : -1) + " to " + ((l.End != null) ? l.End.Index : -1)); + propsrecorded = null; + } + + internal void PlayAddLinedef(DeserializerStream ds) + { + int index; ds.rInt(out index); + //LogRecordInfo("PLY: Removing linedef " + index); + Linedef l = General.Map.Map.GetLinedefByIndex(index); + if (l.Front != null) l.Front.Marked = true; + if (l.Back != null) l.Back.Marked = true; + l.Dispose(); + geometrychanged = true; + } + + internal void RecRemLinedef(Linedef l) + { + if (!BeginRecordData(StreamCodes.RemLinedef)) return; + ss.wInt(l.Index); + ss.wInt(l.Start.Index); + ss.wInt(l.End.Index); + l.ReadWrite(ss); + EndRecordData(); + + //LogRecordInfo("REC: Removing linedef " + l.Index + " (from " + ((l.Start != null) ? l.Start.Index : -1) + " to " + ((l.End != null) ? l.End.Index : -1) + ")"); + propsrecorded = null; + } + + internal void PlayRemLinedef(DeserializerStream ds) + { + int index; ds.rInt(out index); + int sindex; ds.rInt(out sindex); + int eindex; ds.rInt(out eindex); + //LogRecordInfo("PLY: Adding linedef " + index + " from " + sindex + " to " + eindex); + Vertex vs = General.Map.Map.GetVertexByIndex(sindex); + Vertex ve = General.Map.Map.GetVertexByIndex(eindex); + Linedef l = General.Map.Map.CreateLinedef(index, vs, ve); + l.ReadWrite(ds); + l.Marked = true; + geometrychanged = true; + } + + internal void RecPrpLinedef(Linedef l) + { + if (!ignorepropchanges && !isrecordingcommand && !object.ReferenceEquals(l, propsrecorded)) + { + if (!BeginRecordData(StreamCodes.PrpLinedef)) return; + ss.wInt(l.Index); + l.ReadWrite(ss); + EndRecordData(); + propsrecorded = l; + } + } + + internal void PlayPrpLinedef(DeserializerStream ds) + { + int index; ds.rInt(out index); + Linedef l = General.Map.Map.GetLinedefByIndex(index); + l.ReadWrite(ds); + l.Marked = true; + } + + internal void RecRefLinedefStart(Linedef l) + { + if (!BeginRecordData(StreamCodes.RefLinedefStart)) return; + ss.wInt(l.Index); + if (l.Start != null) ss.wInt(l.Start.Index); else ss.wInt(-1); + EndRecordData(); + + //LogRecordInfo("REC: Setting linedef " + l.Index + " start vertex " + ((l.Start != null) ? l.Start.Index : -1)); + propsrecorded = null; + } + + internal void PlayRefLinedefStart(DeserializerStream ds) + { + int index; ds.rInt(out index); + Linedef l = General.Map.Map.GetLinedefByIndex(index); + int vindex; ds.rInt(out vindex); + //LogRecordInfo("PLY: Setting linedef " + index + " start vertex " + vindex); + Vertex v = (vindex >= 0) ? General.Map.Map.GetVertexByIndex(vindex) : null; + l.SetStartVertex(v); + l.Marked = true; + if (v != null) v.Marked = true; + geometrychanged = true; + } + + internal void RecRefLinedefEnd(Linedef l) + { + if (!BeginRecordData(StreamCodes.RefLinedefEnd)) return; + ss.wInt(l.Index); + if (l.End != null) ss.wInt(l.End.Index); else ss.wInt(-1); + EndRecordData(); + + //LogRecordInfo("REC: Setting linedef " + l.Index + " end vertex " + ((l.End != null) ? l.End.Index : -1)); + propsrecorded = null; + } + + internal void PlayRefLinedefEnd(DeserializerStream ds) + { + int index; ds.rInt(out index); + Linedef l = General.Map.Map.GetLinedefByIndex(index); + int vindex; ds.rInt(out vindex); + //LogRecordInfo("PLY: Setting linedef " + index + " end vertex " + vindex); + Vertex v = (vindex >= 0) ? General.Map.Map.GetVertexByIndex(vindex) : null; + l.SetEndVertex(v); + l.Marked = true; + if (v != null) v.Marked = true; + geometrychanged = true; + } + + internal void RecRefLinedefFront(Linedef l) + { + if (!BeginRecordData(StreamCodes.RefLinedefFront)) return; + ss.wInt(l.Index); + if (l.Front != null) ss.wInt(l.Front.Index); else ss.wInt(-1); + EndRecordData(); + + //LogRecordInfo("REC: Setting linedef " + l.Index + " front sidedef " + ((l.Front != null) ? l.Front.Index : -1)); + propsrecorded = null; + } + + internal void PlayRefLinedefFront(DeserializerStream ds) + { + int index; ds.rInt(out index); + Linedef l = General.Map.Map.GetLinedefByIndex(index); + int sindex; ds.rInt(out sindex); + //LogRecordInfo("PLY: Setting linedef " + index + " front sidedef " + sindex); + Sidedef sd = (sindex >= 0) ? General.Map.Map.GetSidedefByIndex(sindex) : null; + l.AttachFront(sd); + l.Marked = true; + if (sd != null) sd.Marked = true; + geometrychanged = true; + } + + internal void RecRefLinedefBack(Linedef l) + { + if (!BeginRecordData(StreamCodes.RefLinedefBack)) return; + ss.wInt(l.Index); + if (l.Back != null) ss.wInt(l.Back.Index); else ss.wInt(-1); + EndRecordData(); + + //LogRecordInfo("REC: Setting linedef " + l.Index + " back sidedef " + ((l.Back != null) ? l.Back.Index : -1)); + propsrecorded = null; + } + + internal void PlayRefLinedefBack(DeserializerStream ds) + { + int index; ds.rInt(out index); + Linedef l = General.Map.Map.GetLinedefByIndex(index); + int sindex; ds.rInt(out sindex); + //LogRecordInfo("PLY: Setting linedef " + index + " back sidedef " + sindex); + Sidedef sd = (sindex >= 0) ? General.Map.Map.GetSidedefByIndex(sindex) : null; + l.AttachBack(sd); + l.Marked = true; + if (sd != null) sd.Marked = true; + geometrychanged = true; + } + + internal void RecAddSidedef(Sidedef s) + { + if (!BeginRecordData(StreamCodes.AddSidedef)) return; + ss.wInt(s.Index); + EndRecordData(); + + //LogRecordInfo("REC: Adding sidedef " + s.Index + " to linedef " + s.Line.Index + (s.IsFront ? " front" : " back") + " and sector " + s.Sector.Index); + propsrecorded = null; + } + + internal void PlayAddSidedef(DeserializerStream ds) + { + int index; ds.rInt(out index); + //LogRecordInfo("PLY: Removing sidedef " + index); + Sidedef s = General.Map.Map.GetSidedefByIndex(index); + if (s.Sector != null) s.Sector.Marked = true; + s.Dispose(); + geometrychanged = true; + } + + internal void RecRemSidedef(Sidedef s) + { + if (!BeginRecordData(StreamCodes.RemSidedef)) return; + ss.wInt(s.Index); + ss.wInt(s.Line.Index); + ss.wBool(s.IsFront); + ss.wInt(s.Sector.Index); + s.ReadWrite(ss); + EndRecordData(); + + //LogRecordInfo("REC: Removing sidedef " + s.Index + " (from linedef " + s.Line.Index + (s.IsFront ? " front" : " back") + " and sector " + s.Sector.Index + ")"); + propsrecorded = null; + } + + internal void PlayRemSidedef(DeserializerStream ds) + { + int index; ds.rInt(out index); + int dindex; ds.rInt(out dindex); + bool front; ds.rBool(out front); + int sindex; ds.rInt(out sindex); + //LogRecordInfo("PLY: Adding sidedef " + index + " to linedef " + dindex + (front ? " front" : " back") + " and sector " + sindex); + Linedef l = General.Map.Map.GetLinedefByIndex(dindex); + Sector s = General.Map.Map.GetSectorByIndex(sindex); + Sidedef sd = General.Map.Map.CreateSidedef(index, l, front, s); + sd.ReadWrite(ds); + sd.Marked = true; + geometrychanged = true; + } + + internal void RecPrpSidedef(Sidedef s) + { + if (!ignorepropchanges && !isrecordingcommand && !object.ReferenceEquals(s, propsrecorded)) + { + if (!BeginRecordData(StreamCodes.PrpSidedef)) return; + ss.wInt(s.Index); + s.ReadWrite(ss); + EndRecordData(); + propsrecorded = s; + } + } + + internal void PlayPrpSidedef(DeserializerStream ds) + { + int index; ds.rInt(out index); + Sidedef s = General.Map.Map.GetSidedefByIndex(index); + s.ReadWrite(ds); + s.Marked = true; + } + + internal void RecRefSidedefSector(Sidedef s) + { + if (!BeginRecordData(StreamCodes.RefSidedefSector)) return; + ss.wInt(s.Index); + if (s.Sector != null) ss.wInt(s.Sector.Index); else ss.wInt(-1); + EndRecordData(); + + //LogRecordInfo("REC: Setting sidedef " + s.Index + " sector " + ((s.Sector != null) ? s.Sector.Index : -1)); + propsrecorded = null; + } + + internal void PlayRefSidedefSector(DeserializerStream ds) + { + int index; ds.rInt(out index); + Sidedef sd = General.Map.Map.GetSidedefByIndex(index); + int sindex; ds.rInt(out sindex); + //LogRecordInfo("PLY: Setting sidedef " + index + " sector " + sindex); + Sector sc = (sindex >= 0) ? General.Map.Map.GetSectorByIndex(sindex) : null; + sd.SetSector(sc); + sd.Marked = true; + if (sc != null) sc.Marked = true; + geometrychanged = true; + } + + internal void RecAddSector(Sector s) + { + if (!BeginRecordData(StreamCodes.AddSector)) return; + ss.wInt(s.Index); + EndRecordData(); + + //LogRecordInfo("REC: Adding sector " + s.Index); + propsrecorded = null; + } + + internal void PlayAddSector(DeserializerStream ds) + { + int index; ds.rInt(out index); + //LogRecordInfo("PLY: Removing sector " + index); + Sector s = General.Map.Map.GetSectorByIndex(index); + s.Dispose(); + geometrychanged = true; + } + + internal void RecRemSector(Sector s) + { + if (!BeginRecordData(StreamCodes.RemSector)) return; + ss.wInt(s.Index); + s.ReadWrite(ss); + EndRecordData(); + + //LogRecordInfo("REC: Removing sector " + s.Index); + propsrecorded = null; + } + + internal void PlayRemSector(DeserializerStream ds) + { + int index; ds.rInt(out index); + //LogRecordInfo("PLY: Adding sector " + index); + Sector s = General.Map.Map.CreateSector(index); + s.ReadWrite(ds); + s.Marked = true; + geometrychanged = true; + } + + internal void RecPrpSector(Sector s) + { + if (!ignorepropchanges && !isrecordingcommand && !object.ReferenceEquals(s, propsrecorded)) + { + if (!BeginRecordData(StreamCodes.PrpSector)) return; + ss.wInt(s.Index); + s.ReadWrite(ss); + EndRecordData(); + propsrecorded = s; + } + } + + internal void PlayPrpSector(DeserializerStream ds) + { + int index; ds.rInt(out index); + Sector s = General.Map.Map.GetSectorByIndex(index); + s.ReadWrite(ds); + s.Marked = true; + } + + internal void RecAddThing(Thing t) + { + if (!BeginRecordData(StreamCodes.AddThing)) return; + ss.wInt(t.Index); + EndRecordData(); + + //LogRecordInfo("REC: Adding thing " + t.Index); + propsrecorded = null; + } + + internal void PlayAddThing(DeserializerStream ds) + { + int index; ds.rInt(out index); + //LogRecordInfo("PLY: Removing thing " + index); + Thing t = General.Map.Map.GetThingByIndex(index); + t.Dispose(); + populationchanged = true; + } + + internal void RecRemThing(Thing t) + { + if (!BeginRecordData(StreamCodes.RemThing)) return; + ss.wInt(t.Index); + t.ReadWrite(ss); + EndRecordData(); + + //LogRecordInfo("REC: Removing thing " + t.Index); + propsrecorded = null; + } + + internal void PlayRemThing(DeserializerStream ds) + { + int index; ds.rInt(out index); + //LogRecordInfo("PLY: Adding thing " + index); + Thing t = General.Map.Map.CreateThing(index); + t.ReadWrite(ds); + t.Marked = true; + populationchanged = true; + } + + internal void RecPrpThing(Thing t) + { + if (!ignorepropchanges && !isrecordingcommand && !object.ReferenceEquals(t, propsrecorded)) + { + if (!BeginRecordData(StreamCodes.PrpThing)) return; + ss.wInt(t.Index); + t.ReadWrite(ss); + EndRecordData(); + propsrecorded = t; + } + } + + internal void PlayPrpThing(DeserializerStream ds) + { + int index; ds.rInt(out index); + Thing t = General.Map.Map.GetThingByIndex(index); + t.ReadWrite(ds); + t.Marked = true; + } + + #endregion + } +} diff --git a/Source/Core/Editing/UndoSnapshot.cs b/Source/Core/Editing/UndoSnapshot.cs new file mode 100644 index 0000000..74bf650 --- /dev/null +++ b/Source/Core/Editing/UndoSnapshot.cs @@ -0,0 +1,169 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using CodeImp.DoomBuilder.Windows; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Rendering; +using System.Diagnostics; +using CodeImp.DoomBuilder.Actions; +using ICSharpCode.SharpZipLib.BZip2; + +#endregion + +namespace CodeImp.DoomBuilder.Editing +{ + public class UndoSnapshot + { + #region ================== Variables + + private MemoryStream recstream; + private string filename; + private string description; + private int ticketid; // For safe withdrawing + private volatile bool storeondisk; + private volatile bool isondisk; + private bool isdisposed; + private Dictionary customdata; + + #endregion + + #region ================== Properties + + public string Description { get { return description; } set { description = value; } } + public int TicketID { get { return ticketid; } } + internal bool StoreOnDisk { get { return storeondisk; } set { storeondisk = value; } } + public bool IsOnDisk { get { return isondisk; } } + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + internal UndoSnapshot(string description, MemoryStream recstream, int ticketid) + { + if (recstream == null) General.Fail("Argument cannot be null!"); + this.ticketid = ticketid; + this.description = description; + this.recstream = recstream; + this.filename = null; + } + + // Constructor + internal UndoSnapshot(UndoSnapshot info, MemoryStream recstream) + { + if (recstream == null) General.Fail("Argument cannot be null!"); + this.ticketid = info.ticketid; + this.description = info.description; + this.recstream = recstream; + this.filename = null; + } + + // Disposer + internal void Dispose() + { + lock (this) + { + isdisposed = true; + if (recstream != null) recstream.Dispose(); + recstream = null; + if (isondisk) File.Delete(filename); + isondisk = false; + } + } + + #endregion + + #region ================== Methods + + // This returns the map data + internal MemoryStream GetStream() + { + lock (this) + { + // Restore into memory if needed + if (isondisk) RestoreFromFile(); + + // Return the buffer + return recstream; + } + } + + // This moves the snapshot from memory to harddisk + internal void WriteToFile() + { + lock (this) + { + if (isdisposed) return; + if (isondisk) return; + isondisk = true; + + // Compress data + recstream.Seek(0, SeekOrigin.Begin); + MemoryStream outstream = new MemoryStream((int)recstream.Length); + BZip2.Compress(recstream, outstream, 300000); + + // Make temporary file + filename = General.MakeTempFilename(General.Map.TempPath, "snapshot"); + + // Write data to file + File.WriteAllBytes(filename, outstream.ToArray()); + + // Remove data from memory + recstream.Dispose(); + recstream = null; + outstream.Dispose(); + } + } + + // This loads the snapshot from harddisk into memory + internal void RestoreFromFile() + { + lock (this) + { + if (isdisposed) return; + if (!isondisk) return; + isondisk = false; + + // Read the file data + MemoryStream instream = new MemoryStream(File.ReadAllBytes(filename)); + + // Decompress data + MemoryStream outstream = new MemoryStream((int)instream.Length * 4); + instream.Seek(0, SeekOrigin.Begin); + BZip2.Decompress(instream, outstream); + recstream = new MemoryStream(outstream.ToArray()); + + // Clean up + instream.Dispose(); + File.Delete(filename); + filename = null; + } + } + + #endregion + } +} diff --git a/Source/Core/General/BinaryHeap.cs b/Source/Core/General/BinaryHeap.cs new file mode 100644 index 0000000..24bc6cf --- /dev/null +++ b/Source/Core/General/BinaryHeap.cs @@ -0,0 +1,260 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Runtime.InteropServices; +using System.Diagnostics; + +#endregion + +namespace CodeImp.DoomBuilder +{ + internal class BinaryHeap : IEnumerable, ICollection where T : IComparable + { + #region ================== Variables + + // This will keep all items + private List heap; + + #endregion + + #region ================== Properties + + public int Count { get { return heap.Count; } } + public virtual bool IsReadOnly { get { return false; } } + public T Root { get { if (heap.Count > 0) return heap[0]; else return default(T); } } + public T this[int index] { get { return ItemAt(index); } } + + #endregion + + #region ================== Constructor / Destructor + + // Constructor + public BinaryHeap() + { + // Initialize with default capacity + heap = new List(); + } + + // Constructor + public BinaryHeap(int capacity) + { + // Initialize with specified capacity + heap = new List(capacity); + } + + // Destructor + ~BinaryHeap() + { + // Clean up + heap = null; + } + + #endregion + + #region ================== Methods + + // Methods to find our way through the heap + private int ParentOf(int index) { return (index - 1) >> 1; } + private int LeftOf(int index) { return (index << 1) + 1; } + private int RightOf(int index) { return (index << 1) + 2; } + + // This swaps two items in place + protected virtual void SwapItems(int index1, int index2) + { + // Swap items + T tempitem = heap[index1]; + heap[index1] = heap[index2]; + heap[index2] = tempitem; + } + + // This adds an item to the list + // This is an O(log n) operation, where n is Count + public virtual void Add(T item) + { + int index = heap.Count; + + // Add to the end of the heap + heap.Add(item); + + // Continue until the item is at the top + // or compares higher to the parent item + while ((index > 0) && (heap[index].CompareTo(heap[ParentOf(index)]) > 0)) + { + // Swap with parent item + SwapItems(index, ParentOf(index)); + index = ParentOf(index); + } + } + + // Finds and returns the index of an item + // This is an O(n) operation, where n is Count + public virtual int IndexOf(T item) + { + return heap.IndexOf(item); + } + + // This returns the item at the given index + // This is an O(1) operation + public virtual T ItemAt(int index) + { + return heap[index]; + } + + // This removes an item from the list + // This is an O(log n) operation, where n is Count + public virtual void RemoveAt(int index) + { + int newindex = index; + + // Replace with last item + heap[index] = heap[heap.Count - 1]; + heap.RemoveAt(heap.Count - 1); + + // Continue while item has at least a left child + while (LeftOf(index) < heap.Count) + { + // Right childs also available? + if (RightOf(index) < heap.Count) + { + // Compare with both childs + // NOTE: Using newindex as indexer in the second line to ensure the lowest of both is chosen + if (heap[index].CompareTo(heap[LeftOf(index)]) < 0) newindex = LeftOf(index); + if (heap[newindex].CompareTo(heap[RightOf(index)]) < 0) newindex = RightOf(index); + } + // Only left child available + else + { + // Compare with left child + if (heap[index].CompareTo(heap[LeftOf(index)]) < 0) newindex = LeftOf(index); + } + + // Item should move down? + if (newindex != index) + { + // Swap the items + SwapItems(index, newindex); + index = newindex; + } + else + { + // Item is fine where it is, we're done + break; + } + } + } + + // This removes the root item from the list + // This is an O(log n) operation, where n is Count + public virtual void RemoveRoot() + { + // Remove the root item + RemoveAt(0); + } + + // This removes a specific item from the list + // This is an O(n) operation, where n is Count + public virtual bool Remove(T item) + { + // Find the item in the heap + int index = IndexOf(item); + if (index > -1) + { + // Remove the item from the heap + RemoveAt(index); + return true; + } + else + { + // No such item + return false; + } + } + + // This clears the heap + public virtual void Clear() + { + // Clear the heap + heap.Clear(); + } + + // This checks if the heap contains a specific item + // This is an O(n) operation, where n is Count + public virtual bool Contains(T item) + { + return (IndexOf(item) > -1); + } + + // This copies all items to an array + public virtual void CopyTo(T[] array) + { + // Copy items + heap.CopyTo(array); + } + + // This copies all items to an array + public virtual void CopyTo(T[] array, int arrayindex) + { + // Copy items + heap.CopyTo(array, arrayindex); + } + + // This copies all items to an array + public virtual void CopyTo(int index, T[] array, int arrayindex, int count) + { + // Copy items + heap.CopyTo(index, array, arrayindex, count); + } + + // Implemented to display the list + // This is an O(n) operation, where n is Count + public override string ToString() + { + StringBuilder str = new StringBuilder(heap.Count * 5); + + // Go for all items + for (int i = 0; i < heap.Count; i++) + { + // Append item to string + if (i > 0) str.Append(", "); + str.Append(heap[i]); + } + + // Return the string + return str.ToString(); + } + + // This returns an enumerator + public IEnumerator GetEnumerator() + { + return heap.GetEnumerator(); + } + + // This returns an enumerator + IEnumerator IEnumerable.GetEnumerator() + { + return heap.GetEnumerator(); + } + + #endregion + } +} diff --git a/Source/Core/General/CRC.cs b/Source/Core/General/CRC.cs new file mode 100644 index 0000000..00b8ddd --- /dev/null +++ b/Source/Core/General/CRC.cs @@ -0,0 +1,90 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Runtime.InteropServices; +using System.Diagnostics; +using ICSharpCode.SharpZipLib.Checksums; + +#endregion + +namespace CodeImp.DoomBuilder +{ + public class CRC + { + #region ================== Constants + + #endregion + + #region ================== Variables + + private Crc32 crc; + + #endregion + + #region ================== Properties + + public long Value { get { return crc.Value; } } + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + public CRC() + { + crc = new Crc32(); + + // We have no destructor + GC.SuppressFinalize(this); + } + + #endregion + + #region ================== Methods + + public void Add(long value) + { + uint lo = (uint)((ulong)value & 0x00000000FFFFFFFF); + uint hi = (uint)(((ulong)value & 0xFFFFFFFF00000000) >> 32); + crc.Update(unchecked((int)lo)); + crc.Update(unchecked((int)hi)); + } + + public void Add(int value) + { + crc.Update(value); + } + + public void Add(byte[] data) + { + crc.Update(data); + } + + public void Reset() + { + crc.Reset(); + } + + #endregion + } +} diff --git a/Source/Core/General/Clock.cs b/Source/Core/General/Clock.cs new file mode 100644 index 0000000..d9f2f2e --- /dev/null +++ b/Source/Core/General/Clock.cs @@ -0,0 +1,162 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Runtime.InteropServices; +using System.Diagnostics; + +#endregion + +namespace CodeImp.DoomBuilder +{ + public class Clock + { + #region ================== Declarations + + //#if !LINUX + + [DllImport("kernel32.dll")] + private static extern short QueryPerformanceCounter(ref long x); + + [DllImport("kernel32.dll")] + private static extern short QueryPerformanceFrequency(ref long x); + + //#endif + + #endregion + + #region ================== Constants + + // Set to true enable QPC if possible + private const bool USE_QPC = true; + + // Frequency indicating QPC unavailable + private const long FREQ_NO_QPC = -1; + + #endregion + + #region ================== Variables + + // Settings + private long timefrequency = FREQ_NO_QPC; + private double timescale; + private double currenttime; + + // Disposing + private bool isdisposed = false; + + #endregion + + #region ================== Properties + + // Settings + public bool IsUsingQPC { get { return (timefrequency != FREQ_NO_QPC); } } + public double CurrentTime { get { return currenttime; } } + + // Disposing + public bool IsDisposed { get { return isdisposed; } } + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + public Clock() + { + // Only windows has QPC + //#if !LINUX + if (Environment.OSVersion.Platform != PlatformID.Unix) + { + // Get the high resolution clock frequency + if ((QueryPerformanceFrequency(ref timefrequency) == 0) || !USE_QPC) + { + // No high resolution clock available + timefrequency = FREQ_NO_QPC; + } + else + { + // Calculate the time scale + timescale = (1d / (double)timefrequency) * 1000d; + } + } + //#endif + + // We have no destructor + GC.SuppressFinalize(this); + } + + // Disposer + public void Dispose() + { + // Not already disposed? + if (!isdisposed) + { + // Clean up + + // Done + isdisposed = true; + } + } + + #endregion + + #region ================== Methods + + // This queries the system for the current time + public double GetCurrentTime() + { + // Only windows has QPC + //#if !LINUX + + long timecount = 0; + + // High resolution clock available? + if (timefrequency != FREQ_NO_QPC) + { + // Get the high resolution count + QueryPerformanceCounter(ref timecount); + + // Calculate high resolution time in milliseconds + currenttime = (double)timecount * timescale; + } + else + { + // Use standard clock + currenttime = (double)Environment.TickCount; + } + + /* + #else + + // In LINUX always use standard clock + currenttime = (double)Environment.TickCount; + + #endif + */ + + // Return the current time + return currenttime; + } + + #endregion + } +} diff --git a/Source/Core/General/ErrorItem.cs b/Source/Core/General/ErrorItem.cs new file mode 100644 index 0000000..30dee9a --- /dev/null +++ b/Source/Core/General/ErrorItem.cs @@ -0,0 +1,55 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Runtime.InteropServices; +using System.Diagnostics; + +#endregion + +namespace CodeImp.DoomBuilder +{ + internal struct ErrorItem + { + public ErrorType type; + public string message; + + internal ErrorItem(ErrorType type, string message) + { + this.type = type; + this.message = message; + } + } + + public enum ErrorType + { + /// + /// This indicates a significant error that may cause problems for the data displayed or editing behaviour. + /// + Error, + + /// + /// This indicates a potential problem. + /// + Warning + } +} diff --git a/Source/Core/General/ErrorLogger.cs b/Source/Core/General/ErrorLogger.cs new file mode 100644 index 0000000..53f491a --- /dev/null +++ b/Source/Core/General/ErrorLogger.cs @@ -0,0 +1,116 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Runtime.InteropServices; +using System.Diagnostics; + +#endregion + +namespace CodeImp.DoomBuilder +{ + public class ErrorLogger + { + #region ================== Constants + + #endregion + + #region ================== Variables + + private List errors; + private volatile bool changed; + private volatile bool erroradded; + private volatile bool warningadded; + + #endregion + + #region ================== Properties + + public bool HasErrors { get { return (errors.Count > 0); } } + public bool HasChanged { get { return changed; } set { changed = value; } } + public bool IsErrorAdded { get { return erroradded; } set { erroradded = value; } } + public bool IsWarningAdded { get { return warningadded; } set { warningadded = value; } } + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + internal ErrorLogger() + { + errors = new List(); + } + + #endregion + + #region ================== Methods + + // This clears the errors + public void Clear() + { + lock (this) + { + changed = false; + erroradded = false; + warningadded = false; + errors.Clear(); + } + } + + // This adds a new error + public void Add(ErrorType type, string message) + { + string prefix = ""; + + lock (this) + { + errors.Add(new ErrorItem(type, message)); + switch (type) + { + case ErrorType.Error: + erroradded = true; + prefix = "ERROR: "; + break; + + case ErrorType.Warning: + warningadded = true; + prefix = "WARNING: "; + break; + } + changed = true; + General.WriteLogLine(prefix + message); + } + } + + // This returns the list of errors + internal List GetErrors() + { + lock (this) + { + List copylist = new List(errors); + return copylist; + } + } + + #endregion + } +} diff --git a/Source/Core/General/General.cs b/Source/Core/General/General.cs new file mode 100644 index 0000000..b01c31c --- /dev/null +++ b/Source/Core/General/General.cs @@ -0,0 +1,1727 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using CodeImp.DoomBuilder.Windows; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Geometry; +using System.Runtime.InteropServices; +using CodeImp.DoomBuilder.Actions; +using System.Diagnostics; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Config; +using Microsoft.Win32; +using SlimDX.Direct3D9; +using System.Drawing; +using CodeImp.DoomBuilder.Plugins; +using CodeImp.DoomBuilder.Types; +using System.Collections.ObjectModel; +using System.Threading; +using CodeImp.DoomBuilder.Editing; + +#endregion + +namespace CodeImp.DoomBuilder +{ + public static class General + { + #region ================== API Declarations + + [DllImport("user32.dll")] + internal static extern bool LockWindowUpdate(IntPtr hwnd); + + [DllImport("kernel32.dll", EntryPoint = "RtlZeroMemory", SetLastError = false)] + internal static extern void ZeroMemory(IntPtr dest, int size); + + [DllImport("kernel32.dll", EntryPoint = "RtlMoveMemory", SetLastError = false)] + internal static extern unsafe void CopyMemory(void* dst, void* src, uint length); + + [DllImport("user32.dll", EntryPoint = "SendMessage", SetLastError = true, CallingConvention = CallingConvention.StdCall)] + internal static extern int SendMessage(IntPtr hwnd, uint Msg, int wParam, int lParam); + + [DllImport("user32.dll", SetLastError = true)] + internal static extern bool MessageBeep(MessageBeepType type); + + [DllImport("kernel32.dll")] + internal extern static IntPtr LoadLibrary(string filename); + + [DllImport("kernel32.dll")] + internal extern static bool FreeLibrary(IntPtr moduleptr); + + [DllImport("user32.dll")] + internal static extern IntPtr CreateWindowEx(uint exstyle, string classname, string windowname, uint style, + int x, int y, int width, int height, IntPtr parentptr, int menu, + IntPtr instanceptr, string param); + + [DllImport("user32.dll")] + internal static extern bool DestroyWindow(IntPtr windowptr); + + [DllImport("user32.dll")] + internal static extern int SetWindowPos(IntPtr windowptr, int insertafterptr, int x, int y, int cx, int cy, int flags); + + [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] + private static extern uint GetShortPathName([MarshalAs(UnmanagedType.LPTStr)] string longpath, [MarshalAs(UnmanagedType.LPTStr)] StringBuilder shortpath, uint buffersize); + + [DllImport("user32.dll")] + internal static extern int SetScrollInfo(IntPtr windowptr, int bar, IntPtr scrollinfo, bool redraw); + + [DllImport("user32.dll")] + internal static extern int GetScrollInfo(IntPtr windowptr, int bar, IntPtr scrollinfo); + + #endregion + + #region ================== Constants + + // SendMessage API + internal const int WM_USER = 0x400; + internal const int WM_SYSCOMMAND = 0x112; + internal const int SC_KEYMENU = 0xF100; + internal const int CB_SETITEMHEIGHT = 0x153; + internal const int CB_SHOWDROPDOWN = 0x14F; + internal const int EM_GETSCROLLPOS = WM_USER + 221; + internal const int EM_SETSCROLLPOS = WM_USER + 222; + internal const int SB_HORZ = 0; + internal const int SB_VERT = 1; + internal const int SB_CTL = 2; + internal const int SIF_RANGE = 0x1; + internal const int SIF_PAGE = 0x2; + internal const int SIF_POS = 0x4; + internal const int SIF_DISABLENOSCROLL = 0x8; + internal const int SIF_TRACKPOS = 0x16; + internal const int SIF_ALL = SIF_RANGE + SIF_PAGE + SIF_POS + SIF_TRACKPOS; + + // Files and Folders + private const string SETTINGS_FILE = "Builder64.cfg"; + private const string DEFAULT_SETTINGS_FILE = "Builder64.default.cfg"; + private const string SETTINGS_DIR = "Doom Builder"; + private const string LOG_FILE = "Builder64.log"; + private const string GAME_CONFIGS_DIR = "Configurations"; + private const string COMPILERS_DIR = "Compilers"; + private const string PLUGINS_DIR = "Plugins"; + private const string SCRIPTS_DIR = "Scripting"; + private const string SETUP_DIR = "Setup"; + private const string SPRITES_DIR = "Sprites"; + private const string HELP_FILE = "Refmanual.chm"; + + // SCROLLINFO structure + internal struct ScrollInfo + { + public int size; // size of this structure + public uint mask; // combination of SIF_ constants + public int min; // minimum scrolling position + public int max; // maximum scrolling position + public uint page; // page size (scroll bar uses this value to determine the appropriate size of the proportional scroll box) + public int pos; // position of the scroll box + public int trackpos; // immediate position of a scroll box that the user is dragging + } + + #endregion + + #region ================== Variables + + // Files and Folders + private static string apppath; + private static string setuppath; + private static string settingspath; + private static string logfile; + private static string temppath; + private static string configspath; + private static string compilerspath; + private static string scriptspath; + private static string pluginspath; + private static string spritespath; + + // Main objects + private static Assembly thisasm; + private static MainForm mainwindow; + private static ProgramConfiguration settings; + private static MapManager map; + private static EditingManager editing; + private static ActionManager actions; + private static PluginManager plugins; + private static ColorCollection colors; + private static TypesManager types; + //private static Clock clock; + public static Stopwatch stopwatch; // instead of clock + private static ErrorLogger errorlogger; + private static Mutex appmutex; + + // Configurations + private static List configs; + private static List compilers; + private static List nodebuilders; + private static Dictionary scriptconfigs; + + // States + private static bool debugbuild; + + // Command line arguments + private static string[] cmdargs; + private static string autoloadfile = null; + private static string autoloadmap = null; + private static string autoloadconfig = null; + private static bool delaymainwindow; + + #endregion + + #region ================== Properties + + public static Assembly ThisAssembly { get { return thisasm; } } + public static string AppPath { get { return apppath; } } + public static string TempPath { get { return temppath; } } + public static string ConfigsPath { get { return configspath; } } + public static string CompilersPath { get { return compilerspath; } } + public static string PluginsPath { get { return pluginspath; } } + public static string SpritesPath { get { return spritespath; } } + public static ICollection CommandArgs { get { return Array.AsReadOnly(cmdargs); } } + internal static MainForm MainWindow { get { return mainwindow; } } + public static IMainForm Interface { get { return mainwindow; } } + public static ProgramConfiguration Settings { get { return settings; } } + public static ColorCollection Colors { get { return colors; } } + internal static List Configs { get { return configs; } } + internal static List Nodebuilders { get { return nodebuilders; } } + internal static List Compilers { get { return compilers; } } + internal static Dictionary ScriptConfigs { get { return scriptconfigs; } } + public static MapManager Map { get { return map; } } + public static ActionManager Actions { get { return actions; } } + internal static PluginManager Plugins { get { return plugins; } } + //public static Clock Clock { get { return clock; } } + public static bool DebugBuild { get { return debugbuild; } } + internal static TypesManager Types { get { return types; } } + public static string AutoLoadFile { get { return autoloadfile; } } + public static string AutoLoadMap { get { return autoloadmap; } } + public static string AutoLoadConfig { get { return autoloadconfig; } } + public static bool DelayMainWindow { get { return delaymainwindow; } } + public static EditingManager Editing { get { return editing; } } + public static ErrorLogger ErrorLogger { get { return errorlogger; } } + + #endregion + + #region ================== Configurations + + // This returns the game configuration info by filename + internal static ConfigurationInfo GetConfigurationInfo(string filename) + { + // Go for all config infos + foreach (ConfigurationInfo ci in configs) + { + // Check if filename matches + if (string.Compare(Path.GetFileNameWithoutExtension(ci.Filename), + Path.GetFileNameWithoutExtension(filename), true) == 0) + { + // Return this info + return ci; + } + } + + // None found + return null; + } + + // This loads and returns a game configuration + internal static Configuration LoadGameConfiguration(string filename) + { + Configuration cfg; + + // Make the full filepathname + string filepathname = Path.Combine(configspath, filename); + + // Load configuration + try + { + // Try loading the configuration + cfg = new Configuration(filepathname, true); + + // Check for erors + if (cfg.ErrorResult) + { + // Error in configuration + errorlogger.Add(ErrorType.Error, "Unable to load the game configuration file \"" + filename + "\". " + + "Error in file \"" + cfg.ErrorFile + "\" near line " + cfg.ErrorLine + ": " + cfg.ErrorDescription); + return null; + } + // Check if this is a Doom Builder 2 config + else if (cfg.ReadSetting("type", "") != "Doom Builder 2 Game Configuration") + { + // Old configuration + errorlogger.Add(ErrorType.Error, "Unable to load the game configuration file \"" + filename + "\". " + + "This configuration is not a Doom Builder 2 game configuration."); + return null; + } + else + { + // Return config + return cfg; + } + } + catch (Exception e) + { + // Unable to load configuration + errorlogger.Add(ErrorType.Error, "Unable to load the game configuration file \"" + filename + "\". " + e.GetType().Name + ": " + e.Message); + General.WriteLog(e.StackTrace); + return null; + } + } + + // This loads all game configurations + private static void LoadAllGameConfigurations() + { + Configuration cfg; + string[] filenames; + string name, fullfilename; + + // Display status + mainwindow.DisplayStatus(StatusType.Busy, "Loading game configurations..."); + + // Make array + configs = new List(); + + // Go for all cfg files in the configurations directory + filenames = Directory.GetFiles(configspath, "*.cfg", SearchOption.TopDirectoryOnly); + foreach (string filepath in filenames) + { + // Check if it can be loaded + cfg = LoadGameConfiguration(Path.GetFileName(filepath)); + if (cfg != null) + { + fullfilename = Path.GetFileName(filepath); + ConfigurationInfo cfginfo = new ConfigurationInfo(cfg, fullfilename); + + // Add to lists + General.WriteLogLine("Registered game configuration '" + cfginfo.Name + "' from '" + fullfilename + "'"); + configs.Add(cfginfo); + } + } + + // Sort the list + configs.Sort(); + } + + // This loads all nodebuilder configurations + private static void LoadAllNodebuilderConfigurations() + { + Configuration cfg; + IDictionary builderslist; + string[] filenames; + + // Display status + mainwindow.DisplayStatus(StatusType.Busy, "Loading nodebuilder configurations..."); + + // Make array + nodebuilders = new List(); + + // Go for all cfg files in the compilers directory + filenames = Directory.GetFiles(compilerspath, "*.cfg", SearchOption.AllDirectories); + foreach (string filepath in filenames) + { + try + { + // Try loading the configuration + cfg = new Configuration(filepath, true); + + // Check for erors + if (cfg.ErrorResult) + { + // Error in configuration + errorlogger.Add(ErrorType.Error, "Unable to load the compiler configuration file \"" + Path.GetFileName(filepath) + "\". " + + "Error in file \"" + cfg.ErrorFile + "\" near line " + cfg.ErrorLine + ": " + cfg.ErrorDescription); + } + else + { + // Get structures + builderslist = cfg.ReadSetting("nodebuilders", new Hashtable()); + foreach (DictionaryEntry de in builderslist) + { + // Check if this is a structure + if (de.Value is IDictionary) + { + try + { + // Make nodebuilder info + nodebuilders.Add(new NodebuilderInfo(Path.GetFileName(filepath), de.Key.ToString(), cfg)); + } + catch (Exception e) + { + // Unable to load configuration + errorlogger.Add(ErrorType.Error, "Unable to load the nodebuilder configuration '" + de.Key.ToString() + "' from \"" + Path.GetFileName(filepath) + "\". Error: " + e.Message); + } + } + } + } + } + catch (Exception) + { + // Unable to load configuration + errorlogger.Add(ErrorType.Error, "Unable to load the compiler configuration file \"" + Path.GetFileName(filepath) + "\"."); + } + } + + // Sort the list + nodebuilders.Sort(); + } + + // This loads all script configurations + private static void LoadAllScriptConfigurations() + { + Configuration cfg; + string[] filenames; + + // Display status + mainwindow.DisplayStatus(StatusType.Busy, "Loading script configurations..."); + + // Make collection + scriptconfigs = new Dictionary(); + + // Go for all cfg files in the scripts directory + filenames = Directory.GetFiles(scriptspath, "*.cfg", SearchOption.TopDirectoryOnly); + foreach (string filepath in filenames) + { + try + { + // Try loading the configuration + cfg = new Configuration(filepath, true); + + // Check for erors + if (cfg.ErrorResult) + { + // Error in configuration + errorlogger.Add(ErrorType.Error, "Unable to load the script configuration file \"" + Path.GetFileName(filepath) + "\". " + + "Error in file \"" + cfg.ErrorFile + "\" near line " + cfg.ErrorLine + ": " + cfg.ErrorDescription); + } + else + { + try + { + // Make script configuration + ScriptConfiguration scfg = new ScriptConfiguration(cfg); + string filename = Path.GetFileName(filepath); + scriptconfigs.Add(filename.ToLowerInvariant(), scfg); + } + catch (Exception e) + { + // Unable to load configuration + errorlogger.Add(ErrorType.Error, "Unable to load the script configuration \"" + Path.GetFileName(filepath) + "\". Error: " + e.Message); + } + } + } + catch (Exception e) + { + // Unable to load configuration + errorlogger.Add(ErrorType.Error, "Unable to load the script configuration file \"" + Path.GetFileName(filepath) + "\". Error: " + e.Message); + General.WriteLog(e.StackTrace); + } + } + } + + // This loads all compiler configurations + private static void LoadAllCompilerConfigurations() + { + Configuration cfg; + Dictionary addedcompilers = new Dictionary(); + IDictionary compilerslist; + string[] filenames; + + // Display status + mainwindow.DisplayStatus(StatusType.Busy, "Loading compiler configurations..."); + + // Make array + compilers = new List(); + + // Go for all cfg files in the compilers directory + filenames = Directory.GetFiles(compilerspath, "*.cfg", SearchOption.AllDirectories); + foreach (string filepath in filenames) + { + try + { + // Try loading the configuration + cfg = new Configuration(filepath, true); + + // Check for erors + if (cfg.ErrorResult) + { + // Error in configuration + errorlogger.Add(ErrorType.Error, "Unable to load the compiler configuration file \"" + Path.GetFileName(filepath) + "\". " + + "Error in file \"" + cfg.ErrorFile + "\" near line " + cfg.ErrorLine + ": " + cfg.ErrorDescription); + } + else + { + // Get structures + compilerslist = cfg.ReadSetting("compilers", new Hashtable()); + foreach (DictionaryEntry de in compilerslist) + { + // Check if this is a structure + if (de.Value is IDictionary) + { + // Make compiler info + CompilerInfo info = new CompilerInfo(Path.GetFileName(filepath), de.Key.ToString(), Path.GetDirectoryName(filepath), cfg); + if (!addedcompilers.ContainsKey(info.Name)) + { + compilers.Add(info); + addedcompilers.Add(info.Name, info); + } + else + { + errorlogger.Add(ErrorType.Error, "Compiler \"" + info.Name + "\" is defined more than once. The first definition in " + addedcompilers[info.Name].FileName + " will be used."); + } + } + } + } + } + catch (Exception e) + { + // Unable to load configuration + errorlogger.Add(ErrorType.Error, "Unable to load the compiler configuration file \"" + Path.GetFileName(filepath) + "\". " + e.GetType().Name + ": " + e.Message); + General.WriteLog(e.StackTrace); + } + } + } + + // This returns a nodebuilder by name + internal static NodebuilderInfo GetNodebuilderByName(string name) + { + // Go for all nodebuilders + foreach (NodebuilderInfo n in nodebuilders) + { + // Name matches? + if (n.Name == name) return n; + } + + // Cannot find that nodebuilder + return null; + } + + #endregion + + #region ================== Startup + + // Main program entry + [STAThread] + internal static void Main(string[] args) + { + Uri localpath; + Version thisversion; + + // Determine states +#if DEBUG + debugbuild = true; +#else + debugbuild = false; +#endif + + // Enable OS visual styles + Application.EnableVisualStyles(); + Application.DoEvents(); // This must be here to work around a .NET bug + ToolStripManager.Renderer = new ToolStripProfessionalRenderer(new TanColorTable()); + + // Hook to DLL loading failure event + AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve); + + // Set current thread name + Thread.CurrentThread.Name = "Main Application"; + + // Application is running + appmutex = new Mutex(false, "doombuilder2"); + + // Get a reference to this assembly + thisasm = Assembly.GetExecutingAssembly(); + thisversion = thisasm.GetName().Version; + + // Find application path + localpath = new Uri(Path.GetDirectoryName(thisasm.GetName().CodeBase)); + apppath = Uri.UnescapeDataString(localpath.AbsolutePath); + + // Setup directories + temppath = Path.GetTempPath(); + setuppath = Path.Combine(apppath, SETUP_DIR); + settingspath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), SETTINGS_DIR); + configspath = Path.Combine(apppath, GAME_CONFIGS_DIR); + compilerspath = Path.Combine(apppath, COMPILERS_DIR); + pluginspath = Path.Combine(apppath, PLUGINS_DIR); + scriptspath = Path.Combine(apppath, SCRIPTS_DIR); + spritespath = Path.Combine(apppath, SPRITES_DIR); + logfile = Path.Combine(settingspath, LOG_FILE); + + // Make program settings directory if missing + if (!Directory.Exists(settingspath)) Directory.CreateDirectory(settingspath); + + // Remove the previous log file and start logging + if (File.Exists(logfile)) File.Delete(logfile); + General.WriteLogLine("Doom Builder " + thisversion.Major + "." + thisversion.Minor + " startup"); + General.WriteLogLine("Application path: " + apppath); + General.WriteLogLine("Temporary path: " + temppath); + General.WriteLogLine("Local settings path: " + settingspath); + General.WriteLogLine("Command-line arguments: " + args.Length); + for (int i = 0; i < args.Length; i++) + General.WriteLogLine("Argument " + i + ": \"" + args[i] + "\""); + + // Parse command-line arguments + ParseCommandLineArgs(args); + + // Load configuration + General.WriteLogLine("Loading program configuration..."); + settings = new ProgramConfiguration(); + if (settings.Load(Path.Combine(settingspath, SETTINGS_FILE), + Path.Combine(apppath, DEFAULT_SETTINGS_FILE))) + { + // Create error logger + errorlogger = new ErrorLogger(); + + // Create action manager + actions = new ActionManager(); + + // Bind static methods to actions + General.Actions.BindMethods(typeof(General)); + + // Initialize static classes + MapSet.Initialize(); + + // Create main window + General.WriteLogLine("Loading main interface window..."); + mainwindow = new MainForm(); + mainwindow.SetupInterface(); + mainwindow.UpdateInterface(); + mainwindow.UpdateThingsFilters(); + + if (!delaymainwindow) + { + // Show main window + General.WriteLogLine("Showing main interface window..."); + mainwindow.Show(); + mainwindow.Update(); + } + + // Start Direct3D + General.WriteLogLine("Starting Direct3D graphics driver..."); + try { D3DDevice.Startup(); } + catch (Direct3D9NotFoundException) { AskDownloadDirectX(); return; } + catch (Direct3DX9NotFoundException) { AskDownloadDirectX(); return; } + + // Load plugin manager + General.WriteLogLine("Loading plugins..."); + plugins = new PluginManager(); + plugins.LoadAllPlugins(); + + // Load game configurations + General.WriteLogLine("Loading game configurations..."); + LoadAllGameConfigurations(); + + // Create editing modes + General.WriteLogLine("Creating editing modes manager..."); + editing = new EditingManager(); + + // Now that all settings have been combined (core & plugins) apply the defaults + General.WriteLogLine("Applying configuration settings..."); + actions.ApplyDefaultShortcutKeys(); + mainwindow.ApplyShortcutKeys(); + foreach (ConfigurationInfo info in configs) info.ApplyDefaults(null); + + // Load compiler configurations + General.WriteLogLine("Loading compiler configurations..."); + LoadAllCompilerConfigurations(); + + // Load nodebuilder configurations + General.WriteLogLine("Loading nodebuilder configurations..."); + LoadAllNodebuilderConfigurations(); + + // Load script configurations + General.WriteLogLine("Loading script configurations..."); + LoadAllScriptConfigurations(); + + // Load color settings + General.WriteLogLine("Loading color settings..."); + colors = new ColorCollection(settings.Config); + + // Create application clock + General.WriteLogLine("Creating application clock..."); + //clock = new Clock(); + stopwatch = new Stopwatch(); + stopwatch.Start(); + + // Create types manager + General.WriteLogLine("Creating types manager..."); + types = new TypesManager(); + + // Do auto map loading when window is delayed + if (delaymainwindow) + mainwindow.PerformAutoMapLoading(); + + // All done + General.WriteLogLine("Startup done"); + mainwindow.DisplayReady(); + + // Show any errors if preferred + if (errorlogger.IsErrorAdded) + { + mainwindow.DisplayStatus(StatusType.Warning, "There were errors during program statup!"); + if (!delaymainwindow && General.Settings.ShowErrorsWindow) mainwindow.ShowErrors(); + } + + // Run application from the main window + Application.Run(mainwindow); + } + else + { + // Terminate + Terminate(false); + } + } + + // This handles DLL linking errors + private static System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) + { + // Check if SlimDX failed loading + if (args.Name.Contains("SlimDX")) AskDownloadDirectX(); + + // Return null + return null; + } + + // This asks the user to download DirectX + private static void AskDownloadDirectX() + { + // Cancel loading map from command-line parameters, if any. + // This causes problems, because when the window is shown, the map will + // be loaded and DirectX is initialized (which we seem to be missing) + CancelAutoMapLoad(); + + // Ask the user to download DirectX + if (MessageBox.Show("This application requires the latest version of Microsoft DirectX installed on your computer." + Environment.NewLine + + "Do you want to install and/or update Microsoft DirectX now?", "DirectX Error", System.Windows.Forms.MessageBoxButtons.YesNo, + System.Windows.Forms.MessageBoxIcon.Exclamation) == System.Windows.Forms.DialogResult.Yes) + { + // Open DX web setup + //System.Diagnostics.Process.Start("http://www.microsoft.com/downloads/details.aspx?FamilyId=2DA43D38-DB71-4C1B-BC6A-9B6652CD92A3").WaitForExit(1000); + System.Diagnostics.Process.Start(Path.Combine(setuppath, "dxwebsetup.exe")).WaitForExit(1000); + } + + // End program here + Terminate(false); + } + + // This parses the command line arguments + private static void ParseCommandLineArgs(string[] args) + { + // Keep a copy + cmdargs = args; + + // Make a queue so we can parse the values from left to right + Queue argslist = new Queue(args); + + // Parse list + while (argslist.Count > 0) + { + // Get next arg + string curarg = argslist.Dequeue(); + + // Delay window? + if (string.Compare(curarg, "-DELAYWINDOW", true) == 0) + { + // Delay showing the main window + delaymainwindow = true; + } + // Map name info? + else if (string.Compare(curarg, "-MAP", true) == 0) + { + // Store next arg as map name information + autoloadmap = argslist.Dequeue(); + } + // Config name info? + else if ((string.Compare(curarg, "-CFG", true) == 0) || + (string.Compare(curarg, "-CONFIG", true) == 0)) + { + // Store next arg as config filename information + autoloadconfig = argslist.Dequeue(); + } + // Every other arg + else + { + // No command to load file yet? + if (autoloadfile == null) + { + // Check if this is a file we can load + if (File.Exists(curarg)) + { + // Load this file! + autoloadfile = curarg.Trim(); + } + else + { + // Note in the log that we cannot find this file + General.ErrorLogger.Add(ErrorType.Warning, "Cannot find the specified file \"" + curarg + "\""); + } + } + } + } + } + + // This cancels automatic map loading + internal static void CancelAutoMapLoad() + { + autoloadfile = null; + } + + #endregion + + #region ================== Terminate + + // This is for plugins to use + public static void Exit(bool properexit) + { + // Plugin wants to exit nicely? + if (properexit) + { + // Close dialog forms first + while ((Form.ActiveForm != mainwindow) && (Form.ActiveForm != null)) + Form.ActiveForm.Close(); + + // Close main window + mainwindow.Close(); + } + else + { + // Terminate, no questions asked + Terminate(true); + } + } + + // This terminates the program + internal static void Terminate(bool properexit) + { + // Terminate properly? + if (properexit) + { + General.WriteLogLine("Termination requested"); + + // Unbind static methods from actions + General.Actions.UnbindMethods(typeof(General)); + + // Save colors + colors.SaveColors(settings.Config); + + // Save action controls + actions.SaveSettings(); + + // Save game configuration settings + foreach (ConfigurationInfo ci in configs) ci.SaveSettings(); + + // Save settings configuration + General.WriteLogLine("Saving program configuration..."); + settings.Save(Path.Combine(settingspath, SETTINGS_FILE)); + + // Clean up + if (map != null) map.Dispose(); map = null; + if (editing != null) editing.Dispose(); editing = null; + if (mainwindow != null) mainwindow.Dispose(); + if (actions != null) actions.Dispose(); + //if(clock != null) clock.Dispose(); + if (plugins != null) plugins.Dispose(); + if (types != null) types.Dispose(); + try { D3DDevice.Terminate(); } catch (Exception) { } + + // Application ends here and now + General.WriteLogLine("Termination done"); + Application.Exit(); + } + else + { + // Just end now + General.WriteLogLine("Immediate program termination"); + Application.Exit(); + } + + // Die. + Process.GetCurrentProcess().Kill(); + } + + #endregion + + #region ================== Management + + // This creates a new map + [BeginAction("newmap")] + internal static void NewMap() + { + MapOptions newoptions = new MapOptions(); + MapOptionsForm optionswindow; + + // Cancel volatile mode, if any + General.Editing.DisengageVolatileMode(); + + // Ask the user to save changes (if any) + if (General.AskSaveMap()) + { + // Open map options dialog + optionswindow = new MapOptionsForm(newoptions); + optionswindow.IsForNewMap = true; + if (optionswindow.ShowDialog(mainwindow) == DialogResult.OK) + { + // Display status + mainwindow.DisplayStatus(StatusType.Busy, "Creating new map..."); + Cursor.Current = Cursors.WaitCursor; + + // Clear the display + mainwindow.ClearDisplay(); + + // Trash the current map, if any + if (map != null) map.Dispose(); + + // Let the plugins know + plugins.OnMapNewBegin(); + + // Set this to false so we can see if errors are added + General.ErrorLogger.IsErrorAdded = false; + + // Create map manager with given options + map = new MapManager(); + if (map.InitializeNewMap(newoptions)) + { + // Done + } + else + { + // Unable to create map manager + map.Dispose(); + map = null; + + // Show splash logo on display + mainwindow.ShowSplashDisplay(); + } + + // Let the plugins know + plugins.OnMapNewEnd(); + + // All done + settings.FindDefaultDrawSettings(); + mainwindow.SetupInterface(); + mainwindow.RedrawDisplay(); + mainwindow.UpdateThingsFilters(); + mainwindow.UpdateInterface(); + mainwindow.HideInfo(); + + if (errorlogger.IsErrorAdded) + { + // Show any errors if preferred + mainwindow.DisplayStatus(StatusType.Warning, "There were errors during loading!"); + if (!delaymainwindow && General.Settings.ShowErrorsWindow) mainwindow.ShowErrors(); + } + else + mainwindow.DisplayReady(); + + Cursor.Current = Cursors.Default; + } + } + } + + // This closes the current map + [BeginAction("closemap")] + internal static void ActionCloseMap() { CloseMap(); } + internal static bool CloseMap() + { + // Cancel volatile mode, if any + General.Editing.DisengageVolatileMode(); + + // Ask the user to save changes (if any) + if (General.AskSaveMap()) + { + // Display status + mainwindow.DisplayStatus(StatusType.Busy, "Closing map..."); + General.WriteLogLine("Unloading map..."); + Cursor.Current = Cursors.WaitCursor; + + // Trash the current map + if (map != null) map.Dispose(); + map = null; + + // Clear errors + General.ErrorLogger.Clear(); + + // Show splash logo on display + mainwindow.ShowSplashDisplay(); + + // Done + Cursor.Current = Cursors.Default; + editing.UpdateCurrentEditModes(); + mainwindow.SetupInterface(); + mainwindow.RedrawDisplay(); + mainwindow.HideInfo(); + mainwindow.UpdateThingsFilters(); + mainwindow.UpdateInterface(); + mainwindow.DisplayReady(); + General.WriteLogLine("Map unload done"); + return true; + } + else + { + // User cancelled + return false; + } + } + + // This loads a map from file + [BeginAction("openmap")] + internal static void OpenMap() + { + OpenFileDialog openfile; + + // Cancel volatile mode, if any + General.Editing.DisengageVolatileMode(); + + // Open map file dialog + openfile = new OpenFileDialog(); + openfile.Filter = "Doom WAD Files (*.wad)|*.wad"; + openfile.Title = "Open Map"; + openfile.AddExtension = false; + openfile.CheckFileExists = true; + openfile.Multiselect = false; + openfile.ValidateNames = true; + if (openfile.ShowDialog(mainwindow) == DialogResult.OK) + { + // Update main window + mainwindow.Update(); + + // Open map file + OpenMapFile(openfile.FileName); + } + + openfile.Dispose(); + } + + // This opens the specified file + internal static void OpenMapFile(string filename) + { + OpenMapOptionsForm openmapwindow; + + // Cancel volatile mode, if any + General.Editing.DisengageVolatileMode(); + + // Ask the user to save changes (if any) + if (General.AskSaveMap()) + { + // Open map options dialog + openmapwindow = new OpenMapOptionsForm(filename); + if (openmapwindow.ShowDialog(mainwindow) == DialogResult.OK) + OpenMapFileWithOptions(filename, openmapwindow.Options); + } + } + + // This opens the specified file without dialog + internal static void OpenMapFileWithOptions(string filename, MapOptions options) + { + // Display status + mainwindow.DisplayStatus(StatusType.Busy, "Opening map file..."); + Cursor.Current = Cursors.WaitCursor; + + // Clear the display + mainwindow.ClearDisplay(); + + // Trash the current map, if any + if (map != null) map.Dispose(); + + // Let the plugins know + plugins.OnMapOpenBegin(); + + // Set this to false so we can see if errors are added + General.ErrorLogger.IsErrorAdded = false; + + // Create map manager with given options + map = new MapManager(); + if (map.InitializeOpenMap(filename, options)) + { + // Add recent file + mainwindow.AddRecentFile(filename); + } + else + { + // Unable to create map manager + map.Dispose(); + map = null; + + // Show splash logo on display + mainwindow.ShowSplashDisplay(); + } + + // Let the plugins know + plugins.OnMapOpenEnd(); + + // All done + settings.FindDefaultDrawSettings(); + mainwindow.SetupInterface(); + mainwindow.RedrawDisplay(); + mainwindow.UpdateThingsFilters(); + mainwindow.UpdateInterface(); + mainwindow.HideInfo(); + + if (errorlogger.IsErrorAdded) + { + // Show any errors if preferred + mainwindow.DisplayStatus(StatusType.Warning, "There were errors during loading!"); + if (!delaymainwindow && General.Settings.ShowErrorsWindow) mainwindow.ShowErrors(); + } + else + mainwindow.DisplayReady(); + + Cursor.Current = Cursors.Default; + } + + // This saves the current map + // Returns tre when saved, false when cancelled or failed + [BeginAction("savemap")] + internal static void ActionSaveMap() { SaveMap(); } + internal static bool SaveMap() + { + bool result = false; + + if (map == null) + return false; + + // Cancel volatile mode, if any + General.Editing.DisengageVolatileMode(); + + // Check if a wad file is known + if (map.FilePathName == "") + { + // Call to SaveMapAs + result = SaveMapAs(); + } + else + { + // Display status + mainwindow.DisplayStatus(StatusType.Busy, "Saving map file..."); + Cursor.Current = Cursors.WaitCursor; + + // Set this to false so we can see if errors are added + General.ErrorLogger.IsErrorAdded = false; + + // Save the map + General.Plugins.OnMapSaveBegin(SavePurpose.Normal); + if (map.SaveMap(map.FilePathName, SavePurpose.Normal)) + { + // Add recent file + mainwindow.AddRecentFile(map.FilePathName); + result = true; + } + General.Plugins.OnMapSaveEnd(SavePurpose.Normal); + + // All done + mainwindow.UpdateInterface(); + + if (errorlogger.IsErrorAdded) + { + // Show any errors if preferred + mainwindow.DisplayStatus(StatusType.Warning, "There were errors during saving!"); + if (!delaymainwindow && General.Settings.ShowErrorsWindow) mainwindow.ShowErrors(); + } + else + mainwindow.DisplayStatus(StatusType.Info, "Map saved in " + map.FileTitle + "."); + + Cursor.Current = Cursors.Default; + } + + return result; + } + + + // This saves the current map as a different file + // Returns tre when saved, false when cancelled or failed + [BeginAction("savemapas")] + internal static void ActionSaveMapAs() { SaveMapAs(); } + internal static bool SaveMapAs() + { + SaveFileDialog savefile; + bool result = false; + + if (map == null) + return false; + + // Cancel volatile mode, if any + General.Editing.DisengageVolatileMode(); + + // Show save as dialog + savefile = new SaveFileDialog(); + savefile.Filter = "Doom WAD Files (*.wad)|*.wad"; + savefile.Title = "Save Map As"; + savefile.AddExtension = true; + savefile.CheckPathExists = true; + savefile.OverwritePrompt = true; + savefile.ValidateNames = true; + if (savefile.ShowDialog(mainwindow) == DialogResult.OK) + { + // Check if we're saving to the same file as the original. + // Because some muppets use Save As even when saving to the same file. + string currentfilename = (map.FilePathName.Length > 0) ? Path.GetFullPath(map.FilePathName).ToLowerInvariant() : ""; + string savefilename = Path.GetFullPath(savefile.FileName).ToLowerInvariant(); + if (currentfilename == savefilename) + { + SaveMap(); + } + else + { + // Display status + mainwindow.DisplayStatus(StatusType.Busy, "Saving map file..."); + Cursor.Current = Cursors.WaitCursor; + + // Set this to false so we can see if errors are added + General.ErrorLogger.IsErrorAdded = false; + + // Save the map + General.Plugins.OnMapSaveBegin(SavePurpose.AsNewFile); + if (map.SaveMap(savefile.FileName, SavePurpose.AsNewFile)) + { + // Add recent file + mainwindow.AddRecentFile(map.FilePathName); + result = true; + } + General.Plugins.OnMapSaveEnd(SavePurpose.AsNewFile); + + // All done + mainwindow.UpdateInterface(); + + if (errorlogger.IsErrorAdded) + { + // Show any errors if preferred + mainwindow.DisplayStatus(StatusType.Warning, "There were errors during saving!"); + if (!delaymainwindow && General.Settings.ShowErrorsWindow) mainwindow.ShowErrors(); + } + else + mainwindow.DisplayStatus(StatusType.Info, "Map saved in " + map.FileTitle + "."); + + Cursor.Current = Cursors.Default; + } + } + + savefile.Dispose(); + return result; + } + + + // This saves the current map as a different file + // Returns tre when saved, false when cancelled or failed + [BeginAction("savemapinto")] + internal static void ActionSaveMapInto() { SaveMapInto(); } + internal static bool SaveMapInto() + { + SaveFileDialog savefile; + bool result = false; + + if (map == null) + return false; + + // Cancel volatile mode, if any + General.Editing.DisengageVolatileMode(); + + // Show save as dialog + savefile = new SaveFileDialog(); + savefile.Filter = "Doom WAD Files (*.wad)|*.wad"; + savefile.Title = "Save Map Into"; + savefile.AddExtension = true; + savefile.CheckPathExists = true; + savefile.OverwritePrompt = false; + savefile.ValidateNames = true; + if (savefile.ShowDialog(mainwindow) == DialogResult.OK) + { + // Display status + mainwindow.DisplayStatus(StatusType.Busy, "Saving map file..."); + Cursor.Current = Cursors.WaitCursor; + + // Set this to false so we can see if errors are added + General.ErrorLogger.IsErrorAdded = false; + + // Save the map + General.Plugins.OnMapSaveBegin(SavePurpose.IntoFile); + if (map.SaveMap(savefile.FileName, SavePurpose.IntoFile)) + { + // Add recent file + mainwindow.AddRecentFile(map.FilePathName); + result = true; + } + General.Plugins.OnMapSaveEnd(SavePurpose.IntoFile); + + // All done + mainwindow.UpdateInterface(); + + if (errorlogger.IsErrorAdded) + { + // Show any errors if preferred + mainwindow.DisplayStatus(StatusType.Warning, "There were errors during saving!"); + if (!delaymainwindow && General.Settings.ShowErrorsWindow) mainwindow.ShowErrors(); + } + else + mainwindow.DisplayStatus(StatusType.Info, "Map saved into " + map.FileTitle + "."); + + Cursor.Current = Cursors.Default; + } + + savefile.Dispose(); + return result; + } + + // This asks to save the map if needed + // Returns false when action was cancelled + internal static bool AskSaveMap() + { + DialogResult result; + + // Map open and not saved? + if (map != null) + { + if (map.IsChanged) + { + // Ask to save changes + result = MessageBox.Show(mainwindow, "Do you want to save changes to " + map.FileTitle + " (" + map.Options.CurrentName + ")?", Application.ProductName, MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question); + if (result == DialogResult.Yes) + { + // Save map + if (SaveMap()) + { + // Ask to save changes to scripts + return map.AskSaveScriptChanges(); + } + else + { + // Failed to save map + return false; + } + } + else if (result == DialogResult.Cancel) + { + // Abort + return false; + } + else + { + // Ask to save changes to scripts + return map.AskSaveScriptChanges(); + } + } + else + { + // Ask to save changes to scripts + return map.AskSaveScriptChanges(); + } + } + else + { + return true; + } + } + + #endregion + + #region ================== Debug + + // This shows a major failure + public static void Fail(string message) + { + General.WriteLogLine("FAIL: " + message); + Debug.Fail(message); + Terminate(false); + } + + // This outputs log information + public static void WriteLogLine(string line) + { + // Output to console + Console.WriteLine(line); + + // Write to log file + try { File.AppendAllText(logfile, line + Environment.NewLine); } + catch (Exception) { } + } + + // This outputs log information + public static void WriteLog(string text) + { + // Output to console + Console.Write(text); + + // Write to log file + try { File.AppendAllText(logfile, text); } + catch (Exception) { } + } + + #endregion + + #region ================== Tools + + // This swaps two pointers + public static void Swap(ref T a, ref T b) + { + T t = a; + a = b; + b = t; + } + + // This calculates the bits needed for a number + public static int BitsForInt(int v) + { + int[] LOGTABLE = new int[] { + 0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7 }; + + int r; // r will be lg(v) + int t, tt; + + if (Int2Bool(tt = v >> 16)) + { + r = Int2Bool(t = tt >> 8) ? 24 + LOGTABLE[t] : 16 + LOGTABLE[tt]; + } + else + { + r = Int2Bool(t = v >> 8) ? 8 + LOGTABLE[t] : LOGTABLE[v]; + } + + return r; + } + + // This clamps a value + public static float Clamp(float value, float min, float max) + { + return Math.Min(Math.Max(min, value), max); + } + + // This clamps a value + public static int Clamp(int value, int min, int max) + { + return Math.Min(Math.Max(min, value), max); + } + + // This clamps a value + public static byte Clamp(byte value, byte min, byte max) + { + return Math.Min(Math.Max(min, value), max); + } + + // This returns an element from a collection by index + public static T GetByIndex(ICollection collection, int index) + { + IEnumerator e = collection.GetEnumerator(); + for (int i = -1; i < index; i++) e.MoveNext(); + return e.Current; + } + + // This returns the next power of 2 + public static int NextPowerOf2(int v) + { + int p = 0; + + // Continue increasing until higher than v + while (Math.Pow(2, p) < v) p++; + + // Return power + return (int)Math.Pow(2, p); + } + + // Convert bool to integer + internal static int Bool2Int(bool v) + { + return v ? 1 : 0; + } + + // Convert integer to bool + internal static bool Int2Bool(int v) + { + return (v != 0); + } + + // This shows a message and logs the message + public static DialogResult ShowErrorMessage(string message, MessageBoxButtons buttons) + { + Cursor oldcursor; + DialogResult result; + + // Log the message + WriteLogLine(message); + + // Use normal cursor + oldcursor = Cursor.Current; + Cursor.Current = Cursors.Default; + + // Show message + IWin32Window window = null; + if ((Form.ActiveForm != null) && Form.ActiveForm.Visible) window = Form.ActiveForm; + result = MessageBox.Show(window, message, Application.ProductName, buttons, MessageBoxIcon.Error); + + // Restore old cursor + Cursor.Current = oldcursor; + + // Return result + return result; + } + + // This shows a message and logs the message + public static DialogResult ShowWarningMessage(string message, MessageBoxButtons buttons) + { + return ShowWarningMessage(message, buttons, MessageBoxDefaultButton.Button1); + } + + // This shows a message and logs the message + public static DialogResult ShowWarningMessage(string message, MessageBoxButtons buttons, MessageBoxDefaultButton defaultbutton) + { + Cursor oldcursor; + DialogResult result; + + // Log the message + WriteLogLine(message); + + // Use normal cursor + oldcursor = Cursor.Current; + Cursor.Current = Cursors.Default; + + // Show message + IWin32Window window = null; + if ((Form.ActiveForm != null) && Form.ActiveForm.Visible) window = Form.ActiveForm; + result = MessageBox.Show(window, message, Application.ProductName, buttons, MessageBoxIcon.Warning, defaultbutton); + + // Restore old cursor + Cursor.Current = oldcursor; + + // Return result + return result; + } + + // This shows the reference manual + public static void ShowHelp(string pagefile) + { + ShowHelp(pagefile, HELP_FILE); + } + + // This shows the reference manual + public static void ShowHelp(string pagefile, string chmfile) + { + // Check if the file can be found in the root + string filepathname = Path.Combine(apppath, chmfile); + if (!File.Exists(filepathname)) + { + // Check if the file exists in the plugins directory + filepathname = Path.Combine(pluginspath, chmfile); + if (!File.Exists(filepathname)) + { + // Fail + WriteLogLine("ERROR: Can't find the help file \"" + chmfile + "\""); + return; + } + } + + // Show help file + Help.ShowHelp(mainwindow, filepathname, HelpNavigator.Topic, pagefile); + } + + // This returns a unique temp filename + internal static string MakeTempFilename(string tempdir) + { + return MakeTempFilename(tempdir, "tmp"); + } + + // This returns a unique temp filename + internal static string MakeTempFilename(string tempdir, string extension) + { + string filename; + string chars = "abcdefghijklmnopqrstuvwxyz1234567890"; + Random rnd = new Random(); + int i; + + do + { + // Generate a filename + filename = ""; + for (i = 0; i < 8; i++) filename += chars[rnd.Next(chars.Length)]; + filename = Path.Combine(tempdir, filename + "." + extension); + } + // Continue while file is not unique + while (File.Exists(filename) || Directory.Exists(filename)); + + // Return the filename + return filename; + } + + // This returns a unique temp directory name + internal static string MakeTempDirname() + { + string dirname; + const string chars = "abcdefghijklmnopqrstuvwxyz1234567890"; + Random rnd = new Random(); + int i; + + do + { + // Generate a filename + dirname = ""; + for (i = 0; i < 8; i++) dirname += chars[rnd.Next(chars.Length)]; + dirname = Path.Combine(temppath, dirname); + } + // Continue while file is not unique + while (File.Exists(dirname) || Directory.Exists(dirname)); + + // Return the filename + return dirname; + } + + // This shows an image in a panel either zoomed or centered depending on size + public static void DisplayZoomedImage(Panel panel, Image image) + { + // Set the image + panel.BackgroundImage = image; + + // Image not null? + if (image != null) + { + // Small enough to fit in panel? + if ((image.Size.Width < panel.ClientRectangle.Width) && + (image.Size.Height < panel.ClientRectangle.Height)) + { + // Display centered + panel.BackgroundImageLayout = ImageLayout.Center; + } + else + { + // Display zoomed + panel.BackgroundImageLayout = ImageLayout.Zoom; + } + } + } + + // This calculates the new rectangle when one is scaled into another keeping aspect ratio + public static RectangleF MakeZoomedRect(Size source, RectangleF target) + { + return MakeZoomedRect(new SizeF((int)source.Width, (int)source.Height), target); + } + + // This calculates the new rectangle when one is scaled into another keeping aspect ratio + public static RectangleF MakeZoomedRect(Size source, Rectangle target) + { + return MakeZoomedRect(new SizeF((int)source.Width, (int)source.Height), + new RectangleF((int)target.Left, (int)target.Top, (int)target.Width, (int)target.Height)); + } + + // This calculates the new rectangle when one is scaled into another keeping aspect ratio + public static RectangleF MakeZoomedRect(SizeF source, RectangleF target) + { + float scale; + + // Image fits? + if ((source.Width <= target.Width) && + (source.Height <= target.Height)) + { + // Just center + scale = 1.0f; + } + // Image is wider than tall? + else if ((source.Width - target.Width) > (source.Height - target.Height)) + { + // Scale down by width + scale = target.Width / source.Width; + } + else + { + // Scale down by height + scale = target.Height / source.Height; + } + + // Return centered and scaled + return new RectangleF(target.Left + (target.Width - source.Width * scale) * 0.5f, + target.Top + (target.Height - source.Height * scale) * 0.5f, + source.Width * scale, source.Height * scale); + } + + // This opens a URL in the default browser + public static void OpenWebsite(string url) + { + RegistryKey key = null; + Process p = null; + string browser; + + try + { + // Get the registry key where default browser is stored + key = Registry.ClassesRoot.OpenSubKey(@"HTTP\shell\open\command", false); + + // Trim off quotes + browser = key.GetValue(null).ToString().ToLower().Replace("\"", ""); + + // String doesnt end in EXE? + if (!browser.EndsWith("exe")) + { + // Get rid of everything after the ".exe" + browser = browser.Substring(0, browser.LastIndexOf(".exe") + 4); + } + } + finally + { + // Clean up + if (key != null) key.Close(); + } + + try + { + // Fork a process + p = new Process(); + p.StartInfo.FileName = browser; + p.StartInfo.Arguments = url; + p.Start(); + } + catch (Exception) { } + + // Clean up + if (p != null) p.Dispose(); + } + + // This returns the short path name for a file + public static string GetShortFilePath(string longpath) + { + int maxlen = 256; + StringBuilder shortname = new StringBuilder(maxlen); + uint len = GetShortPathName(longpath, shortname, (uint)maxlen); + return shortname.ToString(); + } + + #endregion + + /* + [BeginAction("testaction")] + internal static void TestAction() + { + ScriptEditorForm t = new ScriptEditorForm(); + t.ShowDialog(mainwindow); + t.Dispose(); + } + */ + } +} + diff --git a/Source/Core/General/Launcher.cs b/Source/Core/General/Launcher.cs new file mode 100644 index 0000000..2c2cfe2 --- /dev/null +++ b/Source/Core/General/Launcher.cs @@ -0,0 +1,357 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.IO; +using CodeImp.DoomBuilder.Data; +using System.Diagnostics; +using CodeImp.DoomBuilder.Actions; +using System.Windows.Forms; +using CodeImp.DoomBuilder.Windows; + +#endregion + +namespace CodeImp.DoomBuilder +{ + internal class Launcher : IDisposable + { + #region ================== Constants + + private const string NUMBERS = "0123456789"; + + #endregion + + #region ================== Variables + + private string tempwad; + + private bool isdisposed; + + #endregion + + #region ================== Properties + + public string TempWAD { get { return tempwad; } } + + #endregion + + #region ================== Constructor / Destructor + + // Constructor + public Launcher(MapManager manager) + { + // Initialize + CleanTempFile(manager); + + // Bind actions + General.Actions.BindMethods(this); + } + + // Disposer + public void Dispose() + { + // Not yet disposed? + if (!isdisposed) + { + // Unbind actions + General.Actions.UnbindMethods(this); + + // Remove temporary file + try { File.Delete(tempwad); } + catch (Exception) { } + + // Done + isdisposed = true; + } + } + + #endregion + + #region ================== Parameters + + // This takes the unconverted parameters (with placeholders) and converts it + // to parameters with full paths, names and numbers where placeholders were put. + // The tempfile must be the full path and filename to the PWAD file to test. + public string ConvertParameters(string parameters, int skill, bool shortpaths) + { + string outp = parameters; + DataLocation iwadloc; + string p_wp = "", p_wf = ""; + string p_ap = "", p_apq = ""; + string p_l1 = "", p_l2 = ""; + string p_nm = ""; + string f = tempwad; + + // Make short path if needed + if (shortpaths) f = General.GetShortFilePath(f); + + // Find the first IWAD file + if (General.Map.Data.FindFirstIWAD(out iwadloc)) + { + // %WP and %WF result in IWAD file + p_wp = iwadloc.location; + p_wf = Path.GetFileName(p_wp); + if (shortpaths) + { + p_wp = General.GetShortFilePath(p_wp); + p_wf = General.GetShortFilePath(p_wf); + } + } + + // Make a list of all data locations, including map location + DataLocation maplocation = new DataLocation(DataLocation.RESOURCE_WAD, General.Map.FilePathName, false, false, false); + DataLocationList locations = new DataLocationList(); + locations.AddRange(General.Map.ConfigSettings.Resources); + locations.AddRange(General.Map.Options.Resources); + locations.Add(maplocation); + + // Go for all data locations + foreach (DataLocation dl in locations) + { + // Location not the IWAD file? + if ((dl.type != DataLocation.RESOURCE_WAD) || (dl.location != iwadloc.location)) + { + // Location not included? + if (!dl.notfortesting) + { + // Add to string of files + if (shortpaths) + { + p_ap += General.GetShortFilePath(dl.location) + " "; + p_apq += "\"" + General.GetShortFilePath(dl.location) + "\" "; + } + else + { + p_ap += dl.location + " "; + p_apq += "\"" + dl.location + "\" "; + } + } + } + } + + // Trim last space from resource file locations + p_ap = p_ap.TrimEnd(' '); + p_apq = p_apq.TrimEnd(' '); + + // Try finding the L1 and L2 numbers from the map name + string numstr = ""; + bool first = true; + foreach (char c in General.Map.Options.CurrentName) + { + // Character is a number? + if (NUMBERS.IndexOf(c) > -1) + { + // Include it + numstr += c; + } + else + { + // Store the number if we found one + if (numstr.Length > 0) + { + int num = 0; + int.TryParse(numstr, out num); + if (first) p_l1 = num.ToString(); else p_l2 = num.ToString(); + numstr = ""; + first = false; + } + } + } + + // Store the number if we found one + if (numstr.Length > 0) + { + int num = 0; + int.TryParse(numstr, out num); + if (first) p_l1 = num.ToString(); else p_l2 = num.ToString(); + } + + // No monsters? + if (!General.Settings.TestMonsters) p_nm = "-nomonsters"; + + // Make sure all our placeholders are in uppercase + outp = outp.Replace("%f", "%F"); + outp = outp.Replace("%wp", "%WP"); + outp = outp.Replace("%wf", "%WF"); + outp = outp.Replace("%wP", "%WP"); + outp = outp.Replace("%wF", "%WF"); + outp = outp.Replace("%Wp", "%WP"); + outp = outp.Replace("%Wf", "%WF"); + outp = outp.Replace("%l1", "%L1"); + outp = outp.Replace("%l2", "%L2"); + outp = outp.Replace("%l", "%L"); + outp = outp.Replace("%ap", "%AP"); + outp = outp.Replace("%aP", "%AP"); + outp = outp.Replace("%Ap", "%AP"); + outp = outp.Replace("%s", "%S"); + outp = outp.Replace("%nM", "%NM"); + outp = outp.Replace("%Nm", "%NM"); + outp = outp.Replace("%nm", "%NM"); + + // Replace placeholders with actual values + outp = outp.Replace("%F", f); + outp = outp.Replace("%WP", p_wp); + outp = outp.Replace("%WF", p_wf); + outp = outp.Replace("%L1", p_l1); + outp = outp.Replace("%L2", p_l2); + outp = outp.Replace("%L", General.Map.Options.CurrentName); + outp = outp.Replace("\"%AP\"", p_apq); + outp = outp.Replace("%AP", p_ap); + outp = outp.Replace("%S", skill.ToString()); + outp = outp.Replace("%NM", p_nm); + + // Return result + return outp; + } + + #endregion + + #region ================== Test + + // This saves the map to a temporary file and launches a test + [BeginAction("testmap")] + public void Test() + { + TestAtSkill(General.Map.ConfigSettings.TestSkill); + } + + // This saves the map to a temporary file and launches a test wit hthe given skill + public void TestAtSkill(int skill) + { + Cursor oldcursor = Cursor.Current; + ProcessStartInfo processinfo; + Process process; + TimeSpan deltatime; + string args; + + // Check if configuration is OK + if ((General.Map.ConfigSettings.TestProgram == "") || + !File.Exists(General.Map.ConfigSettings.TestProgram)) + { + // Show message + Cursor.Current = Cursors.Default; + DialogResult result = General.ShowWarningMessage("Your test program is not set for the current game configuration. Would you like to set up your test program now?", MessageBoxButtons.YesNo); + if (result == DialogResult.Yes) + { + // Show game configuration on the right page + General.MainWindow.ShowConfigurationPage(2); + } + return; + } + + // No custom parameters? + if (!General.Map.ConfigSettings.CustomParameters) + { + // Set parameters to the default ones + General.Map.ConfigSettings.TestParameters = General.Map.Config.TestParameters; + General.Map.ConfigSettings.TestShortPaths = General.Map.Config.TestShortPaths; + } + + // Remove temporary file + try { File.Delete(tempwad); } + catch (Exception) { } + + // Save map to temporary file + Cursor.Current = Cursors.WaitCursor; + tempwad = General.MakeTempFilename(General.Map.TempPath, "wad"); + General.Plugins.OnMapSaveBegin(SavePurpose.Testing); + if (General.Map.SaveMap(tempwad, SavePurpose.Testing)) + { + // No compiler errors? + if (General.Map.Errors.Count == 0) + { + // Make arguments + args = ConvertParameters(General.Map.ConfigSettings.TestParameters, skill, General.Map.ConfigSettings.TestShortPaths); + + // Setup process info + processinfo = new ProcessStartInfo(); + processinfo.Arguments = args; + processinfo.FileName = General.Map.ConfigSettings.TestProgram; + processinfo.CreateNoWindow = false; + processinfo.ErrorDialog = false; + processinfo.UseShellExecute = true; + processinfo.WindowStyle = ProcessWindowStyle.Normal; + processinfo.WorkingDirectory = Path.GetDirectoryName(processinfo.FileName); + + // Output info + General.WriteLogLine("Running test program: " + processinfo.FileName); + General.WriteLogLine("Program parameters: " + processinfo.Arguments); + + // Disable interface + General.MainWindow.DisplayStatus(StatusType.Busy, "Waiting for game application to finish..."); + + try + { + // Start the program + process = Process.Start(processinfo); + + // Wait for program to complete + while (!process.WaitForExit(10)) + { + General.MainWindow.Update(); + } + + // Done + deltatime = TimeSpan.FromTicks(process.ExitTime.Ticks - process.StartTime.Ticks); + General.WriteLogLine("Test program has finished."); + General.WriteLogLine("Run time: " + deltatime.TotalSeconds.ToString("###########0.00") + " seconds"); + } + catch (Exception e) + { + // Unable to start the program + General.ShowErrorMessage("Unable to start the test program, " + e.GetType().Name + ": " + e.Message, MessageBoxButtons.OK); ; + } + + General.MainWindow.DisplayReady(); + } + else + { + General.MainWindow.DisplayStatus(StatusType.Warning, "Unable to test the map due to script errors."); + } + } + + // Clean up temp file + CleanTempFile(General.Map); + + // Done + General.Map.Graphics.Reset(); + General.Plugins.OnMapSaveEnd(SavePurpose.Testing); + General.MainWindow.RedrawDisplay(); + General.MainWindow.FocusDisplay(); + Cursor.Current = oldcursor; + } + + // This deletes the previous temp file and creates a new, empty temp file + private void CleanTempFile(MapManager manager) + { + // Remove temporary file + try { File.Delete(tempwad); } + catch (Exception) { } + + // Make new empty temp file + tempwad = General.MakeTempFilename(manager.TempPath, "wad"); + File.WriteAllText(tempwad, ""); + } + + #endregion + } +} diff --git a/Source/Core/General/MapManager.cs b/Source/Core/General/MapManager.cs new file mode 100644 index 0000000..04e00a5 --- /dev/null +++ b/Source/Core/General/MapManager.cs @@ -0,0 +1,1689 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using System.Diagnostics; +using CodeImp.DoomBuilder.Windows; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Editing; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Data; +using CodeImp.DoomBuilder.Actions; +using CodeImp.DoomBuilder.Config; +using CodeImp.DoomBuilder.Plugins; +using CodeImp.DoomBuilder.Compilers; +using CodeImp.DoomBuilder.VisualModes; + +#endregion + +namespace CodeImp.DoomBuilder +{ + public sealed class MapManager + { + #region ================== Constants + + // Map header name in temporary file + internal const string TEMP_MAP_HEADER = "TEMPMAP"; + internal const string BUILD_MAP_HEADER = "MAP01"; + public const string CONFIG_MAP_HEADER = "~MAP"; + + #endregion + + #region ================== Variables + + // Status + private bool changed; + private bool scriptschanged; + private bool maploading; + + // Map information + private string filetitle; + private string filepathname; + private string temppath; + + // Main objects + private MapSet map; + private MapSetIO io; + private MapOptions options; + private ConfigurationInfo configinfo; + private GameConfiguration config; + private DataManager data; + private D3DDevice graphics; + private Renderer2D renderer2d; + private Renderer3D renderer3d; + private WAD tempwad; + private GridSetup grid; + private UndoManager undoredo; + private CopyPasteManager copypaste; + private Launcher launcher; + private ThingsFilter thingsfilter; + private ScriptEditorForm scriptwindow; + private List errors; + private VisualCamera visualcamera; + + // villsa + private List hashkeys; + private List hashkeynames; + + // Disposing + private bool isdisposed = false; + + #endregion + + #region ================== Properties + + // villsa + public List TextureHashKey { get { return hashkeys; } } + public List TextureHashName { get { return hashkeynames; } } + + public string FilePathName { get { return filepathname; } } + public string FileTitle { get { return filetitle; } } + public string TempPath { get { return temppath; } } + public MapOptions Options { get { return options; } } + public MapSet Map { get { return map; } } + public DataManager Data { get { return data; } } + public bool IsChanged { get { return changed | CheckScriptChanged(); } set { changed |= value; if (!maploading) General.MainWindow.UpdateMapChangedStatus(); } } + public bool IsDisposed { get { return isdisposed; } } + internal D3DDevice Graphics { get { return graphics; } } + public IRenderer2D Renderer2D { get { return renderer2d; } } + public IRenderer3D Renderer3D { get { return renderer3d; } } + internal Renderer2D CRenderer2D { get { return renderer2d; } } + internal Renderer3D CRenderer3D { get { return renderer3d; } } + public GameConfiguration Config { get { return config; } } + internal ConfigurationInfo ConfigSettings { get { return configinfo; } } + public GridSetup Grid { get { return grid; } } + public UndoManager UndoRedo { get { return undoredo; } } + internal CopyPasteManager CopyPaste { get { return copypaste; } } + public IMapSetIO FormatInterface { get { return io; } } + internal Launcher Launcher { get { return launcher; } } + public ThingsFilter ThingsFilter { get { return thingsfilter; } } + internal List Errors { get { return errors; } } + internal ScriptEditorForm ScriptEditor { get { return scriptwindow; } } + public VisualCamera VisualCamera { get { return visualcamera; } set { visualcamera = value; } } + public bool IsScriptsWindowOpen { get { return (scriptwindow != null) && !scriptwindow.IsDisposed; } } + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + internal MapManager() + { + // We have no destructor + GC.SuppressFinalize(this); + + // Create temporary path + temppath = General.MakeTempDirname(); + Directory.CreateDirectory(temppath); + General.WriteLogLine("Temporary directory: " + temppath); + + // Basic objects + grid = new GridSetup(); + undoredo = new UndoManager(); + copypaste = new CopyPasteManager(); + launcher = new Launcher(this); + thingsfilter = new NullThingsFilter(); + errors = new List(); + + // villsa + hashkeys = new List(); + hashkeynames = new List(); + } + + // Disposer + internal bool Dispose() + { + // Not already disposed? + if (!isdisposed) + { + // Let the plugins know + General.Plugins.OnMapCloseBegin(); + + // Stop processing + General.MainWindow.StopProcessing(); + + // Close script editor + CloseScriptEditor(false); + + // Change to no mode + General.Editing.ChangeMode((EditMode)null); + + // Unbind any methods + General.Actions.UnbindMethods(this); + + // Dispose + maploading = true; + if (grid != null) grid.Dispose(); + if (launcher != null) launcher.Dispose(); + if (copypaste != null) copypaste.Dispose(); + if (undoredo != null) undoredo.Dispose(); + General.WriteLogLine("Unloading data resources..."); + if (data != null) data.Dispose(); + General.WriteLogLine("Closing temporary file..."); + if (tempwad != null) tempwad.Dispose(); + General.WriteLogLine("Unloading map data..."); + if (map != null) map.Dispose(); + General.WriteLogLine("Stopping graphics device..."); + if (renderer2d != null) renderer2d.Dispose(); + if (renderer3d != null) renderer3d.Dispose(); + if (graphics != null) graphics.Dispose(); + visualcamera = null; + grid = null; + launcher = null; + copypaste = null; + undoredo = null; + data = null; + tempwad = null; + map = null; + renderer2d = null; + renderer3d = null; + graphics = null; + + // villsa + hashkeys = null; + hashkeynames = null; + + // We may spend some time to clean things up here + GC.Collect(); + + // Remove temp file + General.WriteLogLine("Removing temporary directory..."); + try { Directory.Delete(temppath, true); } + catch (Exception e) + { + General.WriteLogLine(e.GetType().Name + ": " + e.Message); + General.WriteLogLine("Failed to remove temporary directory!"); + } + + // Let the plugins know + General.Plugins.OnMapCloseEnd(); + + // Done + isdisposed = true; + return true; + } + else + { + // Already closed + return true; + } + } + + #endregion + + #region ================== New / Open + + // Initializes for a new map + internal bool InitializeNewMap(MapOptions options) + { + string tempfile; + + // Apply settings + this.filetitle = "unnamed.wad"; + this.filepathname = ""; + this.maploading = true; + this.changed = false; + this.options = options; + + General.WriteLogLine("Creating new map '" + options.CurrentName + "' with configuration '" + options.ConfigFile + "'"); + + // Initiate graphics + General.WriteLogLine("Initializing graphics device..."); + graphics = new D3DDevice(General.MainWindow.Display); + if (!graphics.Initialize()) return false; + + // Create renderers + renderer2d = new Renderer2D(graphics); + renderer3d = new Renderer3D(graphics); + + // Load game configuration + General.WriteLogLine("Loading game configuration..."); + configinfo = General.GetConfigurationInfo(options.ConfigFile); + config = new GameConfiguration(General.LoadGameConfiguration(options.ConfigFile)); + configinfo.ApplyDefaults(config); + General.Editing.UpdateCurrentEditModes(); + + // Create map data + map = new MapSet(); + + // Create temp wadfile + tempfile = General.MakeTempFilename(temppath); + General.WriteLogLine("Creating temporary file: " + tempfile); +#if DEBUG + tempwad = new WAD(tempfile); +#else + try { tempwad = new WAD(tempfile); } + catch (Exception e) + { + General.ShowErrorMessage("Error while creating a temporary wad file:\n" + e.GetType().Name + ": " + e.Message, MessageBoxButtons.OK); + return false; + } +#endif + + // Read the map from temp file + General.WriteLogLine("Initializing map format interface " + config.FormatInterface + "..."); + io = MapSetIO.Create(config.FormatInterface, tempwad, this); + + // Create required lumps + General.WriteLogLine("Creating map data structures..."); + tempwad.Insert(TEMP_MAP_HEADER, 0, 0); + io.Write(map, TEMP_MAP_HEADER, 1); + CreateRequiredLumps(tempwad, TEMP_MAP_HEADER); + + // Load data manager + General.WriteLogLine("Loading data resources..."); + data = new DataManager(); + data.Load(configinfo.Resources, options.Resources); + + // Update structures + options.ApplyGridSettings(); + map.UpdateConfiguration(); + map.Update(); + thingsfilter.Update(); + + // Bind any methods + General.Actions.BindMethods(this); + + // Set defaults + this.visualcamera = new VisualCamera(); + General.Editing.ChangeMode(configinfo.StartMode); + ClassicMode cmode = (General.Editing.Mode as ClassicMode); + if (cmode != null) cmode.SetZoom(0.5f); + renderer2d.SetViewMode((ViewMode)General.Settings.DefaultViewMode); + General.Settings.SetDefaultThingFlags(config.DefaultThingFlags); + + // Success + this.changed = false; + this.maploading = false; + General.WriteLogLine("Map creation done"); + General.MainWindow.UpdateMapChangedStatus(); + return true; + } + + // Initializes for an existing map + internal bool InitializeOpenMap(string filepathname, MapOptions options) + { + WAD mapwad; + string tempfile; + DataLocation maplocation; + + // Apply settings + this.filetitle = Path.GetFileName(filepathname); + this.filepathname = filepathname; + this.changed = false; + this.maploading = true; + this.options = options; + + General.WriteLogLine("Opening map '" + options.CurrentName + "' with configuration '" + options.ConfigFile + "'"); + + // Initiate graphics + General.WriteLogLine("Initializing graphics device..."); + graphics = new D3DDevice(General.MainWindow.Display); + if (!graphics.Initialize()) return false; + + // Create renderers + renderer2d = new Renderer2D(graphics); + renderer3d = new Renderer3D(graphics); + + // Load game configuration + General.WriteLogLine("Loading game configuration..."); + configinfo = General.GetConfigurationInfo(options.ConfigFile); + config = new GameConfiguration(General.LoadGameConfiguration(options.ConfigFile)); + configinfo.ApplyDefaults(config); + General.Editing.UpdateCurrentEditModes(); + + // Create map data + map = new MapSet(); + + // Create temp wadfile + tempfile = General.MakeTempFilename(temppath); + General.WriteLogLine("Creating temporary file: " + tempfile); +#if DEBUG + tempwad = new WAD(tempfile); +#else + try { tempwad = new WAD(tempfile); } + catch (Exception e) + { + General.ShowErrorMessage("Error while creating a temporary wad file:\n" + e.GetType().Name + ": " + e.Message, MessageBoxButtons.OK); + return false; + } +#endif + + // Now open the map file + General.WriteLogLine("Opening source file: " + filepathname); +#if DEBUG + mapwad = new WAD(filepathname, true); +#else + try { mapwad = new WAD(filepathname, true); } + catch (Exception e) + { + General.ShowErrorMessage("Error while opening source wad file:\n" + e.GetType().Name + ": " + e.Message, MessageBoxButtons.OK); + return false; + } +#endif + + // Copy the map lumps to the temp file + General.WriteLogLine("Copying map lumps to temporary file..."); + CopyLumpsByType(mapwad, options.CurrentName, tempwad, TEMP_MAP_HEADER, + true, true, true, true); + + // Close the map file + mapwad.Dispose(); + + // Read the map from temp file + map.BeginAddRemove(); + General.WriteLogLine("Initializing map format interface " + config.FormatInterface + "..."); + io = MapSetIO.Create(config.FormatInterface, tempwad, this); + General.WriteLogLine("Reading map data structures from file..."); +#if DEBUG + map = io.Read(map, TEMP_MAP_HEADER); +#else + try { map = io.Read(map, TEMP_MAP_HEADER); } + catch (Exception e) + { + General.ErrorLogger.Add(ErrorType.Error, "Unable to read the map data structures with the specified configuration. " + e.GetType().Name + ": " + e.Message); + General.ShowErrorMessage("Unable to read the map data structures with the specified configuration.", MessageBoxButtons.OK); + return false; + } +#endif + map.EndAddRemove(); + + // Load data manager + // villsa - load resources before loading map + // texture hash table needs to be initialized first + General.WriteLogLine("Loading data resources..."); + data = new DataManager(); + maplocation = new DataLocation(DataLocation.RESOURCE_WAD, filepathname, options.StrictPatches, false, false); + data.Load(configinfo.Resources, options.Resources, maplocation); + + // Remove unused sectors + map.RemoveUnusedSectors(true); + + // Update structures + options.ApplyGridSettings(); + map.UpdateConfiguration(); + map.SnapAllToAccuracy(); + map.Update(); + thingsfilter.Update(); + + // Bind any methods + General.Actions.BindMethods(this); + + // Set defaults + this.visualcamera = new VisualCamera(); + General.Editing.ChangeMode(configinfo.StartMode); + renderer2d.SetViewMode((ViewMode)General.Settings.DefaultViewMode); + General.Settings.SetDefaultThingFlags(config.DefaultThingFlags); + + // Center map in screen + if (General.Editing.Mode is ClassicMode) (General.Editing.Mode as ClassicMode).CenterInScreen(); + + // Success + this.changed = false; + this.maploading = false; + General.WriteLogLine("Map loading done"); + General.MainWindow.UpdateMapChangedStatus(); + return true; + } + + #endregion + + #region ================== Save + + // Initializes for an existing map + internal bool SaveMap(string newfilepathname, SavePurpose purpose) + { + MapSet outputset; + string nodebuildername, settingsfile; + StatusInfo oldstatus; + WAD targetwad; + int index; + bool includenodes = false; + string origmapname; + bool success = true; + + General.WriteLogLine("Saving map to file: " + newfilepathname); + + // Scripts changed? + bool localscriptschanged = CheckScriptChanged(); + + // If the scripts window is open, save the scripts first + if (IsScriptsWindowOpen) scriptwindow.Editor.ImplicitSave(); + + // Only recompile scripts when the scripts have changed + // (not when only the map changed) + if (localscriptschanged) + { + if (!CompileScriptLumps()) + { + // Compiler failure + if (errors.Count > 0) + General.ShowErrorMessage("Error while compiling scripts: " + errors[0].description, MessageBoxButtons.OK); + else + General.ShowErrorMessage("Unknown compiler error while compiling scripts!", MessageBoxButtons.OK); + } + } + + // Show script window if there are any errors and we are going to test the map + // and always update the errors on the scripts window. + if ((errors.Count > 0) && (scriptwindow == null) && (purpose == SavePurpose.Testing)) ShowScriptEditor(); + if (scriptwindow != null) scriptwindow.Editor.ShowErrors(errors); + + // Only write the map and rebuild nodes when the actual map has changed + // (not when only scripts have changed) + if (changed) + { + // Make a copy of the map data + outputset = map.Clone(); + + // Remove all flags from all 3D Start things + foreach (Thing t in outputset.Things) + { + if (t.Type == config.Start3DModeThingType) + { + // We're not using SetFlag here, this doesn't have to be undone. + // Please note that this is totally exceptional! + List flagkeys = new List(t.Flags.Keys); + foreach (string k in flagkeys) t.Flags[k] = false; + } + } + + // Do we need sidedefs compression? + if (map.Sidedefs.Count > io.MaxSidedefs) + { + // Compress sidedefs + oldstatus = General.MainWindow.Status; + General.MainWindow.DisplayStatus(StatusType.Busy, "Compressing sidedefs..."); + outputset.CompressSidedefs(); + General.MainWindow.DisplayStatus(oldstatus); + + // Check if it still doesnt fit + if (outputset.Sidedefs.Count > io.MaxSidedefs) + { + // Problem! Can't save the map like this! + General.ShowErrorMessage("Unable to save the map: There are too many unique sidedefs!", MessageBoxButtons.OK); + return false; + } + } + + // Check things + if (map.Things.Count > io.MaxThings) + { + General.ShowErrorMessage("Unable to save the map: There are too many things!", MessageBoxButtons.OK); + return false; + } + + // Check sectors + if (map.Sectors.Count > io.MaxSectors) + { + General.ShowErrorMessage("Unable to save the map: There are too many sectors!", MessageBoxButtons.OK); + return false; + } + + // Check linedefs + if (map.Linedefs.Count > io.MaxLinedefs) + { + General.ShowErrorMessage("Unable to save the map: There are too many linedefs!", MessageBoxButtons.OK); + return false; + } + + // Check vertices + if (map.Vertices.Count > io.MaxVertices) + { + General.ShowErrorMessage("Unable to save the map: There are too many vertices!", MessageBoxButtons.OK); + return false; + } + + // TODO: Check for more limitations + + // Write to temporary file + General.WriteLogLine("Writing map data structures to file..."); + index = tempwad.FindLumpIndex(TEMP_MAP_HEADER); + if (index == -1) index = 0; + io.Write(outputset, TEMP_MAP_HEADER, index); + outputset.Dispose(); + + // Get the corresponding nodebuilder + nodebuildername = (purpose == SavePurpose.Testing) ? configinfo.NodebuilderTest : configinfo.NodebuilderSave; + + // Build the nodes + oldstatus = General.MainWindow.Status; + General.MainWindow.DisplayStatus(StatusType.Busy, "Building map nodes..."); + if (!string.IsNullOrEmpty(nodebuildername)) + includenodes = BuildNodes(nodebuildername, true); + else + includenodes = false; + General.MainWindow.DisplayStatus(oldstatus); + } + else + { + // Check if we have nodebuilder lumps + includenodes = VerifyNodebuilderLumps(tempwad, TEMP_MAP_HEADER); + } + + // Suspend data resources + data.Suspend(); + + // Determine original map name + origmapname = (options.PreviousName != "") ? options.PreviousName : options.CurrentName; + + try + { + // Backup existing file, if any + if (File.Exists(newfilepathname)) + { + if (File.Exists(newfilepathname + ".backup3")) File.Delete(newfilepathname + ".backup3"); + if (File.Exists(newfilepathname + ".backup2")) File.Move(newfilepathname + ".backup2", newfilepathname + ".backup3"); + if (File.Exists(newfilepathname + ".backup1")) File.Move(newfilepathname + ".backup1", newfilepathname + ".backup2"); + File.Copy(newfilepathname, newfilepathname + ".backup1"); + } + + // Except when saving INTO another file, + // kill the target file if it is different from source file + if ((purpose != SavePurpose.IntoFile) && (newfilepathname != filepathname)) + { + // Kill target file + if (File.Exists(newfilepathname)) File.Delete(newfilepathname); + + // Kill .dbs settings file + settingsfile = newfilepathname.Substring(0, newfilepathname.Length - 4) + ".dbs"; + if (File.Exists(settingsfile)) File.Delete(settingsfile); + } + + // On Save AS we have to copy the previous file to the new file + if ((purpose == SavePurpose.AsNewFile) && (filepathname != "")) + { + // Copy if original file still exists + if (File.Exists(filepathname)) File.Copy(filepathname, newfilepathname, true); + } + + // If the target file exists, we need to rebuild it + if (File.Exists(newfilepathname)) + { + // Move the target file aside + string origwadfile = newfilepathname + ".temp"; + File.Move(newfilepathname, origwadfile); + + // Open original file + WAD origwad = new WAD(origwadfile, true); + + // Create new target file + targetwad = new WAD(newfilepathname); + + // Copy all lumps, except the original map + CopyAllLumpsExceptMap(origwad, targetwad, origmapname); + + // Close original file and delete it + origwad.Dispose(); + File.Delete(origwadfile); + } + else + { + // Create new target file + targetwad = new WAD(newfilepathname); + } + } + catch (IOException) + { + General.ShowErrorMessage("IO Error while writing target file: " + newfilepathname + ". Please make sure the location is accessible and not in use by another program.", MessageBoxButtons.OK); + data.Resume(); + General.WriteLogLine("Map saving failed"); + return false; + } + catch (UnauthorizedAccessException) + { + General.ShowErrorMessage("Error while accessing target file: " + newfilepathname + ". Please make sure the location is accessible and not in use by another program.", MessageBoxButtons.OK); + data.Resume(); + General.WriteLogLine("Map saving failed"); + return false; + } + + // Copy map lumps to target file + CopyLumpsByType(tempwad, TEMP_MAP_HEADER, targetwad, origmapname, true, true, includenodes, true); + + // Was the map lump name renamed? + if ((options.PreviousName != options.CurrentName) && + (options.PreviousName != "")) + { + General.WriteLogLine("Renaming map lump name from " + options.PreviousName + " to " + options.CurrentName); + + // Find the map header in target + index = targetwad.FindLumpIndex(options.PreviousName); + if (index > -1) + { + // Rename the map lump name + targetwad.Lumps[index].Rename(options.CurrentName); + options.PreviousName = ""; + } + else + { + // Houston, we've got a problem! + General.ShowErrorMessage("Error renaming map lump name: the original map lump could not be found!", MessageBoxButtons.OK); + options.CurrentName = options.PreviousName; + options.PreviousName = ""; + } + } + + // Done with the target file + targetwad.Dispose(); + + // Resume data resources + data.Resume(); + + // Not saved for testing purpose? + if (purpose != SavePurpose.Testing) + { + // Saved in a different file? + if (newfilepathname != filepathname) + { + // Keep new filename + filepathname = newfilepathname; + filetitle = Path.GetFileName(filepathname); + + // Reload resources + ReloadResources(); + } + + try + { + // Open or create the map settings + settingsfile = newfilepathname.Substring(0, newfilepathname.Length - 4) + ".dbs"; + options.WriteConfiguration(settingsfile); + } + catch (Exception e) + { + // Warning only + General.ErrorLogger.Add(ErrorType.Warning, "Could not write the map settings configuration file. " + e.GetType().Name + ": " + e.Message); + } + + // Changes saved + changed = false; + scriptschanged = false; + } + + // Success! + General.WriteLogLine("Map saving done"); + General.MainWindow.UpdateMapChangedStatus(); + return success; + } + + #endregion + + #region ================== Nodebuild + + // This builds the nodes in the temproary file with the given configuration name + private bool BuildNodes(string nodebuildername, bool failaswarning) + { + NodebuilderInfo nodebuilder; + string tempfile1, tempfile2, sourcefile; + bool lumpscomplete = false; + WAD buildwad; + + // Find the nodebuilder + nodebuilder = General.GetNodebuilderByName(nodebuildername); + if (nodebuilder == null) + { + // Problem! Can't find that nodebuilder! + General.ShowWarningMessage("Unable to build the nodes: The configured nodebuilder cannot be found.\nPlease check your game configuration settings!", MessageBoxButtons.OK); + return false; + } + else + { + // Create the compiler interface that will run the nodebuilder + // This automatically creates a temporary directory for us + Compiler compiler = nodebuilder.CreateCompiler(); + + // Make temporary filename + tempfile1 = General.MakeTempFilename(compiler.Location); + + // Make the temporary WAD file + General.WriteLogLine("Creating temporary build file: " + tempfile1); +#if DEBUG + buildwad = new WAD(tempfile1); +#else + try { buildwad = new WAD(tempfile1); } + catch (Exception e) + { + General.ShowErrorMessage("Error while creating a temporary wad file:\n" + e.GetType().Name + ": " + e.Message, MessageBoxButtons.OK); + return false; + } +#endif + + // Determine source file + if (filepathname.Length > 0) + sourcefile = filepathname; + else + sourcefile = tempwad.Filename; + + // Copy lumps to buildwad + General.WriteLogLine("Copying map lumps to temporary build file..."); + CopyLumpsByType(tempwad, TEMP_MAP_HEADER, buildwad, BUILD_MAP_HEADER, true, false, false, true); + + // Close buildwad + buildwad.Dispose(); + + // Does the nodebuilder require an output file? + if (nodebuilder.HasSpecialOutputFile) + { + // Make a temporary output file for the nodebuilder + tempfile2 = General.MakeTempFilename(compiler.Location); + General.WriteLogLine("Temporary output file: " + tempfile2); + } + else + { + // Output file is same as input file + tempfile2 = tempfile1; + } + + // Run the nodebuilder + compiler.Parameters = nodebuilder.Parameters; + compiler.InputFile = Path.GetFileName(tempfile1); + compiler.OutputFile = Path.GetFileName(tempfile2); + compiler.SourceFile = sourcefile; + compiler.WorkingDirectory = Path.GetDirectoryName(tempfile1); + if (compiler.Run()) + { + // Open the output file + try { buildwad = new WAD(tempfile2); } + catch (Exception e) + { + General.WriteLogLine(e.GetType().Name + " while reading build wad file: " + e.Message); + buildwad = null; + } + + if (buildwad != null) + { + // Output lumps complete? + lumpscomplete = VerifyNodebuilderLumps(buildwad, BUILD_MAP_HEADER); + } + + if (lumpscomplete) + { + // Copy nodebuilder lumps to temp file + General.WriteLogLine("Copying nodebuilder lumps to temporary file..."); + CopyLumpsByType(buildwad, BUILD_MAP_HEADER, tempwad, TEMP_MAP_HEADER, false, false, true, false); + } + else + { + // Nodebuilder did not build the lumps! + if (failaswarning) + General.ShowWarningMessage("Unable to build the nodes: The nodebuilder failed to build the expected data structures.\nThe map will be saved without the nodes.", MessageBoxButtons.OK); + else + General.ShowErrorMessage("Unable to build the nodes: The nodebuilder failed to build the expected data structures.", MessageBoxButtons.OK); + } + + // Done with the build wad + if (buildwad != null) buildwad.Dispose(); + } + + // Clean up + compiler.Dispose(); + + // Return result + return lumpscomplete; + } + } + + // This verifies if the nodebuilder lumps exist in a WAD file + private bool VerifyNodebuilderLumps(WAD wad, string mapheader) + { + bool lumpscomplete = false; + + // Find the map header in source + int srcindex = wad.FindLumpIndex(mapheader); + if (srcindex > -1) + { + // Go for all the map lump names + lumpscomplete = true; + foreach (DictionaryEntry ml in config.MapLumpNames) + { + // Read lump settings from map config + bool lumpnodebuild = config.ReadSetting("maplumpnames." + ml.Key + ".nodebuild", false); + bool lumpallowempty = config.ReadSetting("maplumpnames." + ml.Key + ".allowempty", false); + + // Check if this lump should exist + if (lumpnodebuild && !lumpallowempty) + { + // Find the lump in the source + if (wad.FindLump(ml.Key.ToString(), srcindex, srcindex + config.MapLumpNames.Count + 2) == null) + { + // Missing a lump! + lumpscomplete = false; + break; + } + } + } + } + + return lumpscomplete; + } + + #endregion + + #region ================== Lumps + + // This returns a copy of the requested lump stream data + // This is copied from the temp wad file and returns null when the lump is not found + internal MemoryStream GetLumpData(string lumpname) + { + Lump l = tempwad.FindLump(lumpname); + if (l != null) + { + l.Stream.Seek(0, SeekOrigin.Begin); + return new MemoryStream(l.Stream.ReadAllBytes()); + } + else + { + return null; + } + } + + // This writes a copy of the data to a lump in the temp file + internal void SetLumpData(string lumpname, MemoryStream data) + { + int insertindex = tempwad.Lumps.Count; + + // Remove the lump if it already exists + int li = tempwad.FindLumpIndex(lumpname); + if (li > -1) + { + insertindex = li; + tempwad.RemoveAt(li); + } + + // Insert new lump + Lump l = tempwad.Insert(lumpname, insertindex, (int)data.Length); + l.Stream.Seek(0, SeekOrigin.Begin); + data.WriteTo(l.Stream); + + IsChanged = true; + } + + // This creates empty lumps for those required + private void CreateRequiredLumps(WAD target, string mapname) + { + int headerindex, insertindex, targetindex; + string lumpname; + bool lumprequired; + + // Find the map header in target + headerindex = target.FindLumpIndex(mapname); + if (headerindex == -1) + { + // If this header doesnt exists in the target + // then insert at the end of the target + headerindex = target.Lumps.Count; + } + + // Begin inserting at target header index + insertindex = headerindex; + + // Go for all the map lump names + foreach (DictionaryEntry ml in config.MapLumpNames) + { + // Read lump settings from map config + lumprequired = config.ReadSetting("maplumpnames." + ml.Key + ".required", false); + + // Check if this lump is required + if (lumprequired) + { + // Get the lump name + lumpname = ml.Key.ToString(); + if (lumpname == CONFIG_MAP_HEADER) lumpname = mapname; + + // Check if the lump is missing at the target + targetindex = FindSpecificLump(target, lumpname, headerindex, mapname, config.MapLumpNames); + if (targetindex == -1) + { + // Determine target index + insertindex++; + if (insertindex > target.Lumps.Count) insertindex = target.Lumps.Count; + + // Create new, emtpy lump + General.WriteLogLine(lumpname + " is required! Created empty lump."); + target.Insert(lumpname, insertindex, 0); + } + else + { + // Move insert index + insertindex = targetindex; + } + } + } + } + + // This copies all lumps, except those of a specific map + private void CopyAllLumpsExceptMap(WAD source, WAD target, string sourcemapname) + { + // Go for all lumps + bool skipping = false; + foreach (Lump srclump in source.Lumps) + { + // Check if we should stop skipping lumps here + if (skipping && !config.MapLumpNames.Contains(srclump.Name)) + { + // Stop skipping + skipping = false; + } + + // Check if we should start skipping lumps here + if (!skipping && (srclump.Name == sourcemapname)) + { + // We have encountered the map header, start skipping! + skipping = true; + } + + // Not skipping this lump? + if (!skipping) + { + // Copy lump over! + Lump tgtlump = target.Insert(srclump.Name, target.Lumps.Count, srclump.Length); + srclump.CopyTo(tgtlump); + } + } + } + + // This copies specific map lumps from one WAD to another + private void CopyLumpsByType(WAD source, string sourcemapname, + WAD target, string targetmapname, + bool copyrequired, bool copyblindcopy, + bool copynodebuild, bool copyscript) + { + bool lumprequired, lumpblindcopy, lumpnodebuild; + string lumpscript, srclumpname, tgtlumpname; + int srcheaderindex, tgtheaderindex, targetindex, sourceindex, lumpindex; + Lump lump, newlump; + + // Find the map header in target + tgtheaderindex = target.FindLumpIndex(targetmapname); + if (tgtheaderindex == -1) + { + // If this header doesnt exists in the target + // then insert at the end of the target + tgtheaderindex = target.Lumps.Count; + } + + // Begin inserting at target header index + targetindex = tgtheaderindex; + + // Find the map header in source + srcheaderindex = source.FindLumpIndex(sourcemapname); + if (srcheaderindex > -1) + { + // Copy the map header from source to target + //newlump = target.Insert(targetmapname, tgtindex++, source.Lumps[srcindex].Length); + //source.Lumps[srcindex].CopyTo(newlump); + + // Go for all the map lump names + foreach (DictionaryEntry ml in config.MapLumpNames) + { + // Read lump settings from map config + lumprequired = config.ReadSetting("maplumpnames." + ml.Key + ".required", false); + lumpblindcopy = config.ReadSetting("maplumpnames." + ml.Key + ".blindcopy", false); + lumpnodebuild = config.ReadSetting("maplumpnames." + ml.Key + ".nodebuild", false); + lumpscript = config.ReadSetting("maplumpnames." + ml.Key + ".script", ""); + + // Check if this lump should be copied + if ((lumprequired && copyrequired) || (lumpblindcopy && copyblindcopy) || + (lumpnodebuild && copynodebuild) || ((lumpscript.Length != 0) && copyscript)) + { + // Get the lump name + srclumpname = ml.Key.ToString(); + tgtlumpname = ml.Key.ToString(); + if (srclumpname == CONFIG_MAP_HEADER) srclumpname = sourcemapname; + if (tgtlumpname == CONFIG_MAP_HEADER) tgtlumpname = targetmapname; + + // Find the lump in the source + sourceindex = FindSpecificLump(source, srclumpname, srcheaderindex, sourcemapname, config.MapLumpNames); + if (sourceindex > -1) + { + // Remove lump at target + lumpindex = RemoveSpecificLump(target, tgtlumpname, tgtheaderindex, targetmapname, config.MapLumpNames); + + // Determine target index + // When original lump was found and removed then insert at that position + // otherwise insert after last insertion position + if (lumpindex > -1) targetindex = lumpindex; else targetindex++; + if (targetindex > target.Lumps.Count) targetindex = target.Lumps.Count; + + // Copy the lump to the target + //General.WriteLogLine(srclumpname + " copying as " + tgtlumpname); + lump = source.Lumps[sourceindex]; + newlump = target.Insert(tgtlumpname, targetindex, lump.Length); + lump.CopyTo(newlump); + } + else + { + // We don't want to bother the user with this. There are a lot of lumps in + // the game configs that are trivial and don't need to be found. + if (lumprequired) + { + General.ErrorLogger.Add(ErrorType.Warning, ml.Key.ToString() + " (required lump) should be read but was not found in the WAD file."); + } + } + } + } + } + } + + // This finds a lump within the range of known lump names + // Returns -1 when the lump cannot be found + internal static int FindSpecificLump(WAD source, string lumpname, int mapheaderindex, string mapheadername, IDictionary maplumps) + { + // Use the configured map lump names to find the specific lump within range, + // because when an unknown lump is met, this search must stop. + + // Go for all lumps in order to find the specified lump + for (int i = 0; i < maplumps.Count + 1; i++) + { + // Still within bounds? + if ((mapheaderindex + i) < source.Lumps.Count) + { + // Check if this is a known lump name + if (maplumps.Contains(source.Lumps[mapheaderindex + i].Name) || + (maplumps.Contains(CONFIG_MAP_HEADER) && (source.Lumps[mapheaderindex + i].Name == mapheadername))) + { + // Is this the lump we are looking for? + if (source.Lumps[mapheaderindex + i].Name == lumpname) + { + // Return this index + return mapheaderindex + i; + } + } + else + { + // Unknown lump hit, abort search + break; + } + } + } + + // Nothing found + return -1; + } + + // This removes a specific lump and returns the position where the lump was removed + // Returns -1 when the lump could not be found + internal static int RemoveSpecificLump(WAD source, string lumpname, int mapheaderindex, string mapheadername, IDictionary maplumps) + { + int lumpindex; + + // Find the specific lump index + lumpindex = FindSpecificLump(source, lumpname, mapheaderindex, mapheadername, maplumps); + if (lumpindex > -1) + { + // Remove this lump + //General.WriteLogLine(lumpname + " removed"); + source.RemoveAt(lumpindex); + } + else + { + // Lump not found + //General.ErrorLogger.Add(ErrorType.Warning, lumpname + " should be removed but was not found!"); + } + + // Return result + return lumpindex; + } + + #endregion + + #region ================== Selection Groups + + // This adds selection to a group + private void AddSelectionToGroup(int groupindex) + { + General.Interface.SetCursor(Cursors.WaitCursor); + + // Make undo + undoredo.CreateUndo("Assign to group " + groupindex); + + // Make selection + map.AddSelectionToGroup(0x01 << groupindex); + + General.Interface.DisplayStatus(StatusType.Action, "Assigned selection to group " + groupindex); + General.Interface.SetCursor(Cursors.Default); + } + + // This selects a group + private void SelectGroup(int groupindex) + { + // Select + int groupmask = 0x01 << groupindex; + map.SelectVerticesByGroup(groupmask); + map.SelectLinedefsByGroup(groupmask); + map.SelectSectorsByGroup(groupmask); + map.SelectThingsByGroup(groupmask); + + // Redraw to show selection + General.Interface.DisplayStatus(StatusType.Action, "Selected group " + groupindex); + General.Interface.RedrawDisplay(); + } + + // Select actions + [BeginAction("selectgroup1")] internal void SelectGroup1() { SelectGroup(0); } + [BeginAction("selectgroup2")] internal void SelectGroup2() { SelectGroup(1); } + [BeginAction("selectgroup3")] internal void SelectGroup3() { SelectGroup(2); } + [BeginAction("selectgroup4")] internal void SelectGroup4() { SelectGroup(3); } + [BeginAction("selectgroup5")] internal void SelectGroup5() { SelectGroup(4); } + [BeginAction("selectgroup6")] internal void SelectGroup6() { SelectGroup(5); } + [BeginAction("selectgroup7")] internal void SelectGroup7() { SelectGroup(6); } + [BeginAction("selectgroup8")] internal void SelectGroup8() { SelectGroup(7); } + [BeginAction("selectgroup9")] internal void SelectGroup9() { SelectGroup(8); } + [BeginAction("selectgroup10")] internal void SelectGroup10() { SelectGroup(9); } + + // Assign actions + [BeginAction("assigngroup1")] internal void AssignGroup1() { AddSelectionToGroup(0); } + [BeginAction("assigngroup2")] internal void AssignGroup2() { AddSelectionToGroup(1); } + [BeginAction("assigngroup3")] internal void AssignGroup3() { AddSelectionToGroup(2); } + [BeginAction("assigngroup4")] internal void AssignGroup4() { AddSelectionToGroup(3); } + [BeginAction("assigngroup5")] internal void AssignGroup5() { AddSelectionToGroup(4); } + [BeginAction("assigngroup6")] internal void AssignGroup6() { AddSelectionToGroup(5); } + [BeginAction("assigngroup7")] internal void AssignGroup7() { AddSelectionToGroup(6); } + [BeginAction("assigngroup8")] internal void AssignGroup8() { AddSelectionToGroup(7); } + [BeginAction("assigngroup9")] internal void AssignGroup9() { AddSelectionToGroup(8); } + [BeginAction("assigngroup10")] internal void AssignGroup10() { AddSelectionToGroup(9); } + + #endregion + + #region ================== Script Editing + + // Show the script editor + [BeginAction("openscripteditor")] + internal void ShowScriptEditor() + { + Cursor.Current = Cursors.WaitCursor; + + if (scriptwindow == null) + { + // Load the window + + // DON'T USE + /*if (General.Map.FormatInterface.InDoom64Mode) // villsa + { + MapInfoForm mapinfowindow = new MapInfoForm(); + DialogResult result = mapinfowindow.ShowDialog(MainForm.ActiveForm); + mapinfowindow.Dispose(); + } + else*/ + scriptwindow = new ScriptEditorForm(); + } + + //if (!General.Map.FormatInterface.InDoom64Mode) // villsa + //{ + // Window not yet visible? + if (!scriptwindow.Visible) + { + // Show the window + if (General.Settings.ScriptOnTop) + { + if (scriptwindow.Visible && (scriptwindow.Owner == null)) scriptwindow.Hide(); + scriptwindow.Show(General.MainWindow); + } + else + { + if (scriptwindow.Visible && (scriptwindow.Owner != null)) scriptwindow.Hide(); + scriptwindow.Show(); + } + } + scriptwindow.Activate(); + scriptwindow.Focus(); + //} + Cursor.Current = Cursors.Default; + } + + // This asks the user to save changes in script files + // Returns false when cancelled by the user + internal bool AskSaveScriptChanges() + { + // Window open? + if (scriptwindow != null) + { + // Ask to save changes + // This also saves implicitly + return scriptwindow.AskSaveAll(); + } + else + { + // No problems + return true; + } + } + + // This applies the changed status for internal scripts + internal void ApplyScriptChanged() + { + // Remember if lumps are changed + scriptschanged |= scriptwindow.Editor.CheckImplicitChanges(); + } + + // Close the script editor + // Specify true for the closing parameter when + // the window is already in the closing process + internal void CloseScriptEditor(bool closing) + { + if (scriptwindow != null) + { + if (!scriptwindow.IsDisposed) + { + // Remember what files were open + scriptwindow.Editor.WriteOpenFilesToConfiguration(); + + // Close now + if (!closing) scriptwindow.Close(); + } + + // Done + scriptwindow = null; + } + } + + // This checks if the scripts are changed + internal bool CheckScriptChanged() + { + if (scriptwindow != null) + { + // Check if scripts are changed + return scriptschanged || scriptwindow.Editor.CheckImplicitChanges(); + } + else + { + // Check if scripts are changed + return scriptschanged; + } + } + + // This compiles all lumps that require compiling and stores the results + // Returns true when our code worked properly (even when the compiler returned errors) + private bool CompileScriptLumps() + { + bool success = true; + errors.Clear(); + + // Go for all the map lumps + foreach (MapLumpInfo lumpinfo in config.MapLumps.Values) + { + // Is this a script lump? + if (lumpinfo.script != null) + { + // Compile it now + success &= CompileLump(lumpinfo.name, false); + } + } + return success; + } + + // This compiles a script lump and returns any errors that may have occurred + // Returns true when our code worked properly (even when the compiler returned errors) + internal bool CompileLump(string lumpname, bool clearerrors) + { + string inputfile, outputfile, sourcefile; + Compiler compiler; + byte[] filedata; + string reallumpname = lumpname; + + // Find the lump + if (lumpname == CONFIG_MAP_HEADER) reallumpname = TEMP_MAP_HEADER; + Lump lump = tempwad.FindLump(reallumpname); + if (lump == null) throw new Exception("No such lump in temporary wad file '" + reallumpname + "'."); + + // Determine source file + if (filepathname.Length > 0) + sourcefile = filepathname; + else + sourcefile = tempwad.Filename; + + // New list of errors + if (clearerrors) errors.Clear(); + + // Determine the script configuration to use + ScriptConfiguration scriptconfig = config.MapLumps[lumpname].script; + if (scriptconfig.Compiler != null) + { + try + { + // Initialize compiler + compiler = scriptconfig.Compiler.Create(); + } + catch (Exception e) + { + // Fail + errors.Add(new CompilerError("Unable to initialize compiler. " + e.GetType().Name + ": " + e.Message)); + return false; + } + + try + { + // Write lump data to temp script file in compiler's temp directory + inputfile = General.MakeTempFilename(compiler.Location, "tmp"); + lump.Stream.Seek(0, SeekOrigin.Begin); + BinaryReader reader = new BinaryReader(lump.Stream); + File.WriteAllBytes(inputfile, reader.ReadBytes((int)lump.Stream.Length)); + } + catch (Exception e) + { + // Fail + compiler.Dispose(); + errors.Add(new CompilerError("Unable to write script to working file. " + e.GetType().Name + ": " + e.Message)); + return false; + } + + // Make random output filename + outputfile = General.MakeTempFilename(compiler.Location, "tmp"); + + // Run compiler + compiler.Parameters = scriptconfig.Parameters; + compiler.InputFile = Path.GetFileName(inputfile); + compiler.OutputFile = Path.GetFileName(outputfile); + compiler.SourceFile = sourcefile; + compiler.WorkingDirectory = Path.GetDirectoryName(inputfile); + if (compiler.Run()) + { + // Process errors + foreach (CompilerError e in compiler.Errors) + { + CompilerError newerror = e; + + // If the error's filename equals our temporary file, + // use the lump name instead and prefix it with ? + if (string.Compare(e.filename, inputfile, true) == 0) + newerror.filename = "?" + reallumpname; + + errors.Add(newerror); + } + + // No errors? + if (compiler.Errors.Length == 0) + { + // Output file exists? + if (File.Exists(outputfile)) + { + // Copy output file data into a lump? + if (!string.IsNullOrEmpty(scriptconfig.ResultLump)) + { + // Do that now then + try + { + filedata = File.ReadAllBytes(outputfile); + } + catch (Exception e) + { + // Fail + compiler.Dispose(); + errors.Add(new CompilerError("Unable to read compiler output file. " + e.GetType().Name + ": " + e.Message)); + return false; + } + + // Store data + MemoryStream stream = new MemoryStream(filedata); + SetLumpData(scriptconfig.ResultLump, stream); + } + } + } + + // Clean up + compiler.Dispose(); + + // Done + return true; + } + else + { + // Fail + compiler.Dispose(); + errors = null; + return false; + } + } + else + { + // No compiler to run for this script type + return true; + } + } + + // This clears all compiler errors + internal void ClearCompilerErrors() + { + errors.Clear(); + } + + #endregion + + #region ================== Methods + + // This updates everything after the configuration or settings have been changed + internal void UpdateConfiguration() + { + // Update map + map.UpdateConfiguration(); + + // Update settings + renderer3d.CreateProjection(); + + // Things filters + General.MainWindow.UpdateThingsFilters(); + } + + // This changes thing filter + public void ChangeThingFilter(ThingsFilter newfilter) + { + // We have a special filter for null + if (newfilter == null) newfilter = new NullThingsFilter(); + + // Deactivate old filter + if (thingsfilter != null) thingsfilter.Deactivate(); + + // Change + thingsfilter = newfilter; + + // Activate filter + thingsfilter.Activate(); + + // Update interface + General.MainWindow.ReflectThingsFilter(); + + // Redraw + General.MainWindow.RedrawDisplay(); + } + + // This sets a new mapset for editing + internal void ChangeMapSet(MapSet newmap) + { + // Let the plugin and editing mode know + General.Plugins.OnMapSetChangeBegin(); + if (General.Editing.Mode != null) General.Editing.Mode.OnMapSetChangeBegin(); + this.visualcamera.Sector = null; + + // Can't have a selection in an old map set + map.ClearAllSelected(); + + // Reset surfaces + renderer2d.Surfaces.Reset(); + + // Apply + map.Dispose(); + map = newmap; + map.UpdateConfiguration(); + map.SnapAllToAccuracy(); + map.Update(); + thingsfilter.Update(); + + // Let the plugin and editing mode know + General.Plugins.OnMapSetChangeEnd(); + if (General.Editing.Mode != null) General.Editing.Mode.OnMapSetChangeEnd(); + } + + // This reloads resources + [BeginAction("reloadresources")] + internal void DoReloadResource() + { + // Set this to false so we can see if errors are added + General.ErrorLogger.IsErrorAdded = false; + + ReloadResources(); + + if (General.ErrorLogger.IsErrorAdded) + { + // Show any errors if preferred + General.MainWindow.DisplayStatus(StatusType.Warning, "There were errors during resources loading!"); + if (General.Settings.ShowErrorsWindow) General.MainWindow.ShowErrors(); + } + else + General.MainWindow.DisplayReady(); + + } + internal void ReloadResources() + { + DataLocation maplocation; + StatusInfo oldstatus; + Cursor oldcursor; + + // Keep old display info + oldstatus = General.MainWindow.Status; + oldcursor = Cursor.Current; + + // Show status + General.MainWindow.DisplayStatus(StatusType.Busy, "Reloading data resources..."); + Cursor.Current = Cursors.WaitCursor; + + // Clean up + data.Dispose(); + data = null; + config = null; + configinfo = null; + GC.Collect(); + GC.WaitForPendingFinalizers(); + + // Reload game configuration + General.WriteLogLine("Reloading game configuration..."); + configinfo = General.GetConfigurationInfo(options.ConfigFile); + config = new GameConfiguration(General.LoadGameConfiguration(options.ConfigFile)); + General.Editing.UpdateCurrentEditModes(); + + // Reload data resources + General.WriteLogLine("Reloading data resources..."); + data = new DataManager(); + if (!string.IsNullOrEmpty(filepathname)) + { + maplocation = new DataLocation(DataLocation.RESOURCE_WAD, filepathname, false, false, false); + data.Load(configinfo.Resources, options.Resources, maplocation); + } + else + { + data.Load(configinfo.Resources, options.Resources); + } + + // Apply new settings to map elements + map.UpdateConfiguration(); + + // Re-link the background image + grid.LinkBackground(); + + // Inform all plugins that the resources are reloaded + General.Plugins.ReloadResources(); + + // Inform editing mode that the resources are reloaded + if (General.Editing.Mode != null) General.Editing.Mode.OnReloadResources(); + + // Reset status + General.MainWindow.DisplayStatus(oldstatus); + Cursor.Current = oldcursor; + } + + // Game Configuration action + [BeginAction("mapoptions")] + internal void ShowMapOptions() + { + // Cancel volatile mode, if any + General.Editing.DisengageVolatileMode(); + + // Show map options dialog + MapOptionsForm optionsform = new MapOptionsForm(options); + if (optionsform.ShowDialog(General.MainWindow) == DialogResult.OK) + { + // Update interface + General.MainWindow.UpdateInterface(); + + // Stop data manager + data.Dispose(); + + // Apply new options + this.options = optionsform.Options; + + // Load new game configuration + General.WriteLogLine("Loading game configuration..."); + configinfo = General.GetConfigurationInfo(options.ConfigFile); + config = new GameConfiguration(General.LoadGameConfiguration(options.ConfigFile)); + configinfo.ApplyDefaults(config); + General.Editing.UpdateCurrentEditModes(); + + // Setup new map format IO + General.WriteLogLine("Initializing map format interface " + config.FormatInterface + "..."); + io = MapSetIO.Create(config.FormatInterface, tempwad, this); + + // Create required lumps if they don't exist yet + CreateRequiredLumps(tempwad, TEMP_MAP_HEADER); + + // Let the plugins know + General.Plugins.MapReconfigure(); + + // Update interface + General.MainWindow.SetupInterface(); + General.MainWindow.UpdateThingsFilters(); + General.MainWindow.UpdateInterface(); + + // Reload resources + ReloadResources(); + + // Done + General.MainWindow.DisplayReady(); + } + + // Done + optionsform.Dispose(); + } + + // This shows the things filters setup + [BeginAction("thingsfilterssetup")] + internal void ShowThingsFiltersSetup() + { + // Show things filter dialog + ThingsFiltersForm f = new ThingsFiltersForm(); + f.ShowDialog(General.MainWindow); + f.Dispose(); + General.MainWindow.UpdateThingsFilters(); + } + + // This returns true is the given type matches + public bool IsType(Type t) + { + return io.GetType().Equals(t); + } + + #endregion + } +} diff --git a/Source/Core/General/SavePurpose.cs b/Source/Core/General/SavePurpose.cs new file mode 100644 index 0000000..b2294b6 --- /dev/null +++ b/Source/Core/General/SavePurpose.cs @@ -0,0 +1,52 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using System.Diagnostics; +using CodeImp.DoomBuilder.Windows; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Editing; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Data; +using CodeImp.DoomBuilder.Actions; +using CodeImp.DoomBuilder.Config; +using CodeImp.DoomBuilder.Plugins; +using CodeImp.DoomBuilder.Compilers; +using CodeImp.DoomBuilder.VisualModes; + +#endregion + +namespace CodeImp.DoomBuilder +{ + public enum SavePurpose + { + Normal = 0, + AsNewFile = 1, + IntoFile = 2, + Testing = 3 + } +} + diff --git a/Source/Core/General/StepsList.cs b/Source/Core/General/StepsList.cs new file mode 100644 index 0000000..ea4abf1 --- /dev/null +++ b/Source/Core/General/StepsList.cs @@ -0,0 +1,99 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Data; +using System.IO; +using System.Diagnostics; + +#endregion + +namespace CodeImp.DoomBuilder +{ + public class StepsList : List + { + // This returns a step higher + public int GetNextHigher(int level) + { + int low = 0; + int high = base.Count - 1; + + while (low < high) + { + int mid = (int)Math.Floor((float)(low + high) * 0.5f); + int l = base[mid]; + + if (l <= level) + low = mid + 1; + else + high = mid; + } + + return base[high]; + } + + // This returns a step lower + public int GetNextLower(int level) + { + int low = 0; + int high = base.Count - 1; + + while (low < high) + { + int mid = (int)Math.Ceiling((float)(low + high) * 0.5f); + int l = base[mid]; + + if (l >= level) + high = mid - 1; + else + low = mid; + } + + return base[low]; + } + + // This returns the nearest step + public int GetNearest(int level) + { + int low = 0; + int high = base.Count - 1; + + while (low < high) + { + int mid = (int)Math.Floor((float)(low + high) * 0.5f); + int l = base[mid]; + + if (l <= level) + low = mid + 1; + else + high = mid; + } + + // Find which one is nearest + low = (high > 0) ? (high - 1) : 0; + int dlow = level - base[low]; + int dhigh = base[high] - level; + return (dlow < dhigh) ? base[low] : base[high]; + } + } +} diff --git a/Source/Core/Geometry/Angle2D.cs b/Source/Core/Geometry/Angle2D.cs new file mode 100644 index 0000000..83df175 --- /dev/null +++ b/Source/Core/Geometry/Angle2D.cs @@ -0,0 +1,93 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; + +#endregion + +namespace CodeImp.DoomBuilder.Geometry +{ + public struct Angle2D + { + #region ================== Constants + + public const float PI = (float)Math.PI; + public const float PIHALF = (float)Math.PI * 0.5f; + public const float PI2 = (float)Math.PI * 2f; + public const float PIDEG = 57.295779513082320876798154814105f; + public const float SQRT2 = 1.4142135623730950488016887242097f; + + #endregion + + #region ================== Methods + + // This converts doom angle to real angle + public static float DoomToReal(int doomangle) + { + return Normalized(DegToRad((float)(doomangle + 90))); + } + + // This converts real angle to doom angle + public static int RealToDoom(float realangle) + { + return (int)Math.Round(RadToDeg(Normalized(realangle - PIHALF))); + } + + // This converts degrees to radians + public static float DegToRad(float deg) + { + return deg / PIDEG; + } + + // This converts radians to degrees + public static float RadToDeg(float rad) + { + return rad * PIDEG; + } + + // This normalizes an angle + public static float Normalized(float a) + { + while (a < 0f) a += PI2; + while (a >= PI2) a -= PI2; + return a; + } + + // This returns the difference between two angles + public static float Difference(float a, float b) + { + float d; + + // Calculate delta angle + d = Normalized(a) - Normalized(b); + + // Make corrections for zero barrier + if (d < 0f) d += PI2; + if (d > PI) d = PI2 - d; + + // Return result + return d; + } + + #endregion + } +} diff --git a/Source/Core/Geometry/DrawnVertex.cs b/Source/Core/Geometry/DrawnVertex.cs new file mode 100644 index 0000000..94d7910 --- /dev/null +++ b/Source/Core/Geometry/DrawnVertex.cs @@ -0,0 +1,44 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Data; +using System.IO; +using System.Diagnostics; +using System.Windows.Forms; +using CodeImp.DoomBuilder.Map; +using System.Reflection; +using CodeImp.DoomBuilder.Plugins; +using CodeImp.DoomBuilder.Editing; + +#endregion + +namespace CodeImp.DoomBuilder.Geometry +{ + public struct DrawnVertex + { + public Vector2D pos; + public bool stitch; + public bool stitchline; + } +} diff --git a/Source/Core/Geometry/EarClipPolygon.cs b/Source/Core/Geometry/EarClipPolygon.cs new file mode 100644 index 0000000..614b120 --- /dev/null +++ b/Source/Core/Geometry/EarClipPolygon.cs @@ -0,0 +1,202 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using CodeImp.DoomBuilder.Geometry; +using CodeImp.DoomBuilder.Rendering; +using SlimDX.Direct3D9; +using System.Drawing; +using CodeImp.DoomBuilder.Map; + +#endregion + +namespace CodeImp.DoomBuilder.Geometry +{ + public sealed class EarClipPolygon : LinkedList + { + #region ================== Variables + + // Tree variables + private List children; + private bool inner; + + #endregion + + #region ================== Properties + + public List Children { get { return children; } } + public bool Inner { get { return inner; } set { inner = value; } } + + #endregion + + #region ================== Constructors + + // Constructor + internal EarClipPolygon() + { + // Initialize + children = new List(); + } + + // Constructor + internal EarClipPolygon(EarClipPolygon p, EarClipVertex add) : base(p) + { + // Initialize + base.AddLast(add); + children = new List(); + } + + #endregion + + #region ================== Methods + + // This merges a polygon into this one + public void Add(EarClipPolygon p) + { + // Initialize + foreach (EarClipVertex v in p) base.AddLast(v); + } + + // This calculates the area + public float CalculateArea() + { + // Multiply the x coordinate of each vertex by the y coordinate of the next vertex. + // Multiply the y coordinate of each vertex by the x coordinate of the next vertex. + // Subtract these. + float result = 0.0f; + int firstcalculated = 0; + LinkedListNode n1 = base.First; + while (firstcalculated < 2) + { + LinkedListNode n2 = n1.Next ?? base.First; + float a = n1.Value.Position.x * n2.Value.Position.y; + float b = n1.Value.Position.y * n2.Value.Position.x; + result += a - b; + n1 = n2; + if (n2 == base.First) firstcalculated++; + } + return Math.Abs(result / 2.0f); + } + + // This creates a bounding box from the outer polygon + public RectangleF CreateBBox() + { + float left = float.MaxValue; + float right = float.MinValue; + float top = float.MaxValue; + float bottom = float.MinValue; + foreach (EarClipVertex v in this) + { + if (v.Position.x < left) left = v.Position.x; + if (v.Position.x > right) right = v.Position.x; + if (v.Position.y < top) top = v.Position.y; + if (v.Position.y > bottom) bottom = v.Position.y; + } + return new RectangleF(left, top, right - left, bottom - top); + } + + // Point inside the polygon? + // See: http://local.wasp.uwa.edu.au/~pbourke/geometry/insidepoly/ + public bool Intersect(Vector2D p) + { + Vector2D v1 = base.Last.Value.Position; + Vector2D v2; + LinkedListNode n = base.First; + uint c = 0; + + // Go for all vertices + while (n != null) + { + // Get next vertex + v2 = n.Value.Position; + + // Determine min/max values + float miny = Math.Min(v1.y, v2.y); + float maxy = Math.Max(v1.y, v2.y); + float maxx = Math.Max(v1.x, v2.x); + + // Check for intersection + if ((p.y > miny) && (p.y <= maxy)) + { + if (p.x <= maxx) + { + if (v1.y != v2.y) + { + float xint = (p.y - v1.y) * (v2.x - v1.x) / (v2.y - v1.y) + v1.x; + if ((v1.x == v2.x) || (p.x <= xint)) c++; + } + } + } + + // Move to next + v1 = v2; + n = n.Next; + } + + // Inside this polygon? + if ((c & 0x00000001UL) != 0) + { + // Check if not inside the children + foreach (EarClipPolygon child in children) + { + // Inside this child? Then it is not inside this polygon. + if (child.Intersect(p)) return false; + } + + // Inside polygon! + return true; + } + else + { + // Not inside the polygon + return false; + } + } + + // This inserts a polygon if it is a child of this one + public bool InsertChild(EarClipPolygon p) + { + // Polygon must have at least 1 vertex + if (p.Count == 0) return false; + + // Check if it can be inserted at a lower level + foreach (EarClipPolygon child in children) + { + if (child.InsertChild(p)) return true; + } + + // Check if it can be inserted here + if (this.Intersect(p.First.Value.Position)) + { + // Make the polygon the inverse of this one + p.Inner = !inner; + children.Add(p); + return true; + } + + // Can't insert it as a child + return false; + } + + #endregion + } +} diff --git a/Source/Core/Geometry/EarClipVertex.cs b/Source/Core/Geometry/EarClipVertex.cs new file mode 100644 index 0000000..129ba05 --- /dev/null +++ b/Source/Core/Geometry/EarClipVertex.cs @@ -0,0 +1,160 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using CodeImp.DoomBuilder.Geometry; +using CodeImp.DoomBuilder.Rendering; +using SlimDX.Direct3D9; +using System.Drawing; +using CodeImp.DoomBuilder.Map; + +#endregion + +namespace CodeImp.DoomBuilder.Geometry +{ + public sealed class EarClipVertex + { + #region ================== Variables + + // Position + private Vector2D pos; + + // Along a sidedef? + private Sidedef sidedef; + + // Lists + private LinkedListNode vertslink; + private LinkedListNode reflexlink; + private LinkedListNode eartiplink; + + #endregion + + #region ================== Properties + + public Vector2D Position { get { return pos; } } + internal LinkedListNode MainListNode { get { return vertslink; } } + public bool IsReflex { get { return (reflexlink != null); } } + public bool IsEarTip { get { return (eartiplink != null); } } + internal Sidedef Sidedef { get { return sidedef; } set { sidedef = value; } } + + #endregion + + #region ================== Constructor / Disposer + + // Copy constructor + internal EarClipVertex(EarClipVertex v) + { + // Initialize + this.pos = v.pos; + this.sidedef = v.sidedef; + + // We have no destructor + GC.SuppressFinalize(this); + } + + // Copy constructor + internal EarClipVertex(EarClipVertex v, Sidedef sidedef) + { + // Initialize + this.pos = v.pos; + this.sidedef = sidedef; + + // We have no destructor + GC.SuppressFinalize(this); + } + + // Constructor + internal EarClipVertex(Vector2D v, Sidedef sidedef) + { + // Initialize + this.pos = v; + this.sidedef = sidedef; + + // We have no destructor + GC.SuppressFinalize(this); + } + + // Disposer + internal void Dispose() + { + reflexlink = null; + eartiplink = null; + vertslink = null; + sidedef = null; + } + + #endregion + + #region ================== Methods + + // This sets the main linked list node + internal void SetVertsLink(LinkedListNode link) + { + this.vertslink = link; + } + + // This removes the item from all lists + internal void Remove() + { + vertslink.List.Remove(vertslink); + if (reflexlink != null) reflexlink.List.Remove(reflexlink); + if (eartiplink != null) eartiplink.List.Remove(eartiplink); + reflexlink = null; + eartiplink = null; + vertslink = null; + } + + // This adds to reflexes list + public void AddReflex(LinkedList reflexes) + { +#if DEBUG + if(vertslink == null) throw new Exception(); +#endif + if (reflexlink == null) reflexlink = reflexes.AddLast(this); + } + + // This removes from reflexes list + internal void RemoveReflex() + { + if (reflexlink != null) reflexlink.List.Remove(reflexlink); + reflexlink = null; + } + + // This adds to eartips list + internal void AddEarTip(LinkedList eartips) + { +#if DEBUG + if(vertslink == null) throw new Exception(); +#endif + if (eartiplink == null) eartiplink = eartips.AddLast(this); + } + + // This removes from eartips list + internal void RemoveEarTip() + { + if (eartiplink != null) eartiplink.List.Remove(eartiplink); + eartiplink = null; + } + + #endregion + } +} diff --git a/Source/Core/Geometry/LabelPositionInfo.cs b/Source/Core/Geometry/LabelPositionInfo.cs new file mode 100644 index 0000000..2a69d16 --- /dev/null +++ b/Source/Core/Geometry/LabelPositionInfo.cs @@ -0,0 +1,42 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; + +#endregion + +namespace CodeImp.DoomBuilder.Geometry +{ + public struct LabelPositionInfo + { + // Members + public Vector2D position; + public float radius; + + // Constructor + public LabelPositionInfo(Vector2D position, float radius) + { + this.position = position; + this.radius = radius; + } + } +} diff --git a/Source/Core/Geometry/Line2D.cs b/Source/Core/Geometry/Line2D.cs new file mode 100644 index 0000000..1403677 --- /dev/null +++ b/Source/Core/Geometry/Line2D.cs @@ -0,0 +1,281 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; + +#endregion + +namespace CodeImp.DoomBuilder.Geometry +{ + public struct Line2D + { + #region ================== Constants + + #endregion + + #region ================== Variables + + // Coordinates + public Vector2D v1; + public Vector2D v2; + + #endregion + + #region ================== Constructors + + // Constructor + public Line2D(Vector2D v1, Vector2D v2) + { + this.v1 = v1; + this.v2 = v2; + } + + // Constructor + public Line2D(Vector2D v1, float x2, float y2) + { + this.v1 = v1; + this.v2 = new Vector2D(x2, y2); + } + + // Constructor + public Line2D(float x1, float y1, Vector2D v2) + { + this.v1 = new Vector2D(x1, y1); + this.v2 = v2; + } + + // Constructor + public Line2D(float x1, float y1, float x2, float y2) + { + this.v1 = new Vector2D(x1, y1); + this.v2 = new Vector2D(x2, y2); + } + + #endregion + + #region ================== Statics + + // This calculates the length + public static float GetLength(float dx, float dy) + { + // Calculate and return the length + return (float)Math.Sqrt(GetLengthSq(dx, dy)); + } + + // This calculates the square of the length + public static float GetLengthSq(float dx, float dy) + { + // Calculate and return the length + return dx * dx + dy * dy; + } + + // This calculates the normal of a line + public static Vector2D GetNormal(float dx, float dy) + { + return new Vector2D(dx, dy).GetNormal(); + } + + // This tests if the line intersects with the given line coordinates + public static bool GetIntersection(Vector2D v1, Vector2D v2, float x3, float y3, float x4, float y4) + { + float u_ray, u_line; + return GetIntersection(v1, v2, x3, y3, x4, y4, out u_ray, out u_line); + } + + // This tests if the line intersects with the given line coordinates + public static bool GetIntersection(Vector2D v1, Vector2D v2, float x3, float y3, float x4, float y4, out float u_ray) + { + float u_line; + return GetIntersection(v1, v2, x3, y3, x4, y4, out u_ray, out u_line); + } + + // This tests if the line intersects with the given line coordinates + public static bool GetIntersection(Vector2D v1, Vector2D v2, float x3, float y3, float x4, float y4, out float u_ray, out float u_line) + { + // Calculate divider + float div = (y4 - y3) * (v2.x - v1.x) - (x4 - x3) * (v2.y - v1.y); + + // Can this be tested? + if (div != 0.0f) + { + // Calculate the intersection distance from the line + u_line = ((x4 - x3) * (v1.y - y3) - (y4 - y3) * (v1.x - x3)) / div; + + // Calculate the intersection distance from the ray + u_ray = ((v2.x - v1.x) * (v1.y - y3) - (v2.y - v1.y) * (v1.x - x3)) / div; + + // Return if intersecting + return (u_ray >= 0.0f) && (u_ray <= 1.0f) && (u_line >= 0.0f) && (u_line <= 1.0f); + } + else + { + // Unable to detect intersection + u_line = float.NaN; + u_ray = float.NaN; + return false; + } + } + + // This tests on which side of the line the given coordinates are + // returns < 0 for front (right) side, > 0 for back (left) side and 0 if on the line + public static float GetSideOfLine(Vector2D v1, Vector2D v2, Vector2D p) + { + // Calculate and return side information + return (p.y - v1.y) * (v2.x - v1.x) - (p.x - v1.x) * (v2.y - v1.y); + } + + // This returns the shortest distance from given coordinates to line + public static float GetDistanceToLine(Vector2D v1, Vector2D v2, Vector2D p, bool bounded) + { + return (float)Math.Sqrt(GetDistanceToLineSq(v1, v2, p, bounded)); + } + + // This returns the shortest distance from given coordinates to line + public static float GetDistanceToLineSq(Vector2D v1, Vector2D v2, Vector2D p, bool bounded) + { + // Calculate intersection offset + float u = ((p.x - v1.x) * (v2.x - v1.x) + (p.y - v1.y) * (v2.y - v1.y)) / GetLengthSq(v2.x - v1.x, v2.y - v1.y); + + if (bounded) + { + /* + // Limit intersection offset to the line + float lbound = 1f / GetLength(v2.x - v1.x, v2.y - v1.y); + float ubound = 1f - lbound; + if(u < lbound) u = lbound; + if(u > ubound) u = ubound; + */ + if (u < 0f) u = 0f; else if (u > 1f) u = 1f; + } + + // Calculate intersection point + Vector2D i = v1 + u * (v2 - v1); + + // Return distance between intersection and point + // which is the shortest distance to the line + float ldx = p.x - i.x; + float ldy = p.y - i.y; + return ldx * ldx + ldy * ldy; + } + + // This returns the offset coordinates on the line nearest to the given coordinates + public static float GetNearestOnLine(Vector2D v1, Vector2D v2, Vector2D p) + { + // Calculate and return intersection offset + return ((p.x - v1.x) * (v2.x - v1.x) + (p.y - v1.y) * (v2.y - v1.y)) / GetLengthSq(v2.x - v1.x, v2.y - v1.y); + } + + // This returns the coordinates at a specific position on the line + public static Vector2D GetCoordinatesAt(Vector2D v1, Vector2D v2, float u) + { + // Calculate and return intersection offset + return new Vector2D(v1.x + u * (v2.x - v1.x), v1.y + u * (v2.y - v1.y)); + } + + #endregion + + #region ================== Methods + + // This returns the perpendicular vector by simply making a normal + public Vector2D GetPerpendicular() + { + Vector2D d = GetDelta(); + return new Vector2D(-d.y, d.x); + } + + // This calculates the angle + public float GetAngle() + { + // Calculate and return the angle + Vector2D d = GetDelta(); + return -(float)Math.Atan2(-d.y, d.x) + (float)Math.PI * 0.5f; + } + + public Vector2D GetDelta() { return v2 - v1; } + + public float GetLength() { return Line2D.GetLength(v2.x - v1.x, v2.y - v1.y); } + public float GetLengthSq() { return Line2D.GetLengthSq(v2.x - v1.x, v2.y - v1.y); } + + // Output + public override string ToString() + { + return "(" + v1 + ") - (" + v2 + ")"; + } + + public bool GetIntersection(float x3, float y3, float x4, float y4) + { + return Line2D.GetIntersection(v1, v2, x3, y3, x4, y4); + } + + public bool GetIntersection(float x3, float y3, float x4, float y4, out float u_ray) + { + return Line2D.GetIntersection(v1, v2, x3, y3, x4, y4, out u_ray); + } + + public bool GetIntersection(float x3, float y3, float x4, float y4, out float u_ray, out float u_line) + { + return Line2D.GetIntersection(v1, v2, x3, y3, x4, y4, out u_ray, out u_line); + } + + public bool GetIntersection(Line2D ray) + { + return Line2D.GetIntersection(v1, v2, ray.v1.x, ray.v1.y, ray.v2.x, ray.v2.y); + } + + public bool GetIntersection(Line2D ray, out float u_ray) + { + return Line2D.GetIntersection(v1, v2, ray.v1.x, ray.v1.y, ray.v2.x, ray.v2.y, out u_ray); + } + + public bool GetIntersection(Line2D ray, out float u_ray, out float u_line) + { + return Line2D.GetIntersection(v1, v2, ray.v1.x, ray.v1.y, ray.v2.x, ray.v2.y, out u_ray, out u_line); + } + + public float GetSideOfLine(Vector2D p) + { + return Line2D.GetSideOfLine(v1, v2, p); + } + + public float GetDistanceToLine(Vector2D p, bool bounded) + { + return Line2D.GetDistanceToLine(v1, v2, p, bounded); + } + + public float GetDistanceToLineSq(Vector2D p, bool bounded) + { + return Line2D.GetDistanceToLineSq(v1, v2, p, bounded); + } + + public float GetNearestOnLine(Vector2D p) + { + return Line2D.GetNearestOnLine(v1, v2, p); + } + + public Vector2D GetCoordinatesAt(float u) + { + return Line2D.GetCoordinatesAt(v1, v2, u); + } + + #endregion + } +} diff --git a/Source/Core/Geometry/LinedefAngleSorter.cs b/Source/Core/Geometry/LinedefAngleSorter.cs new file mode 100644 index 0000000..920d702 --- /dev/null +++ b/Source/Core/Geometry/LinedefAngleSorter.cs @@ -0,0 +1,94 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using CodeImp.DoomBuilder.Map; + +#endregion + +namespace CodeImp.DoomBuilder.Geometry +{ + public sealed class LinedefAngleSorter : IComparer + { + // Variables + private Linedef baseline; + private bool front; + private Vertex basevertex; + + // Constructor + public LinedefAngleSorter(Linedef baseline, bool front, Vertex fromvertex) + { + // Initialize + this.baseline = baseline; + this.basevertex = fromvertex; + + // Determine rotation direction + if (baseline.End == basevertex) this.front = !front; else this.front = front; + + // We have no destructor + GC.SuppressFinalize(this); + } + + // This calculates the relative angle between two lines + private float CalculateRelativeAngle(Linedef a, Linedef b) + { + float s, n, ana, anb; + Vector2D va, vb; + + // Determine angles + ana = a.Angle; if (a.End == basevertex) ana += Angle2D.PI; + anb = b.Angle; if (b.End == basevertex) anb += Angle2D.PI; + + // Take the difference from angles + n = Angle2D.Difference(ana, anb); + + // Get line end vertices a and b that are not connected to basevertex + if (a.Start == basevertex) va = a.End.Position; else va = a.Start.Position; + if (b.Start == basevertex) vb = b.End.Position; else vb = b.Start.Position; + + // Check to which side the angle goes and adjust angle as needed + s = Line2D.GetSideOfLine(va, vb, basevertex.Position); + if (((s < 0) && front) || ((s > 0) && !front)) n = Angle2D.PI2 - n; + + // Return result + return n; + } + + // Comparer + public int Compare(Linedef x, Linedef y) + { + float ax, ay; + + // Calculate angles + ax = CalculateRelativeAngle(baseline, x); + ay = CalculateRelativeAngle(baseline, y); + + // Compare results + /* + if(ax < ay) return 1; + else if(ax > ay) return -1; + else return 0; + */ + return Math.Sign(ay - ax); + } + } +} diff --git a/Source/Core/Geometry/LinedefSide.cs b/Source/Core/Geometry/LinedefSide.cs new file mode 100644 index 0000000..89a8014 --- /dev/null +++ b/Source/Core/Geometry/LinedefSide.cs @@ -0,0 +1,117 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using CodeImp.DoomBuilder.Geometry; +using CodeImp.DoomBuilder.Rendering; +using SlimDX.Direct3D9; +using System.Drawing; +using CodeImp.DoomBuilder.Map; + +#endregion + +namespace CodeImp.DoomBuilder.Geometry +{ + /// + /// This is used to indicate a side of a line without the need for a sidedef. + /// + public sealed class LinedefSide + { + #region ================== Constants + + #endregion + + #region ================== Variables + + private Linedef line; + private bool front; + + #endregion + + #region ================== Properties + + public Linedef Line { get { return line; } set { line = value; } } + public bool Front { get { return front; } set { front = value; } } + + #endregion + + #region ================== Constructor / Disposer + + /// + /// This is used to indicate a side of a line without the need for a sidedef. + /// + public LinedefSide() + { + // Initialize + + } + + /// + /// This is used to indicate a side of a line without the need for a sidedef. + /// + public LinedefSide(Linedef line, bool front) + { + // Initialize + this.line = line; + this.front = front; + } + + /// + /// This makes a copy of the linedef side. + /// + public LinedefSide(LinedefSide original) + { + // Initialize + this.line = original.line; + this.front = original.front; + } + + // Destructor + ~LinedefSide() + { + } + + #endregion + + #region ================== Methods + + // This compares a linedef side + public static bool operator ==(LinedefSide a, LinedefSide b) + { + if ((object.Equals(a, null)) && (object.Equals(b, null))) return true; + if ((!object.Equals(a, null)) && (object.Equals(b, null))) return false; + if ((object.Equals(a, null)) && (!object.Equals(b, null))) return false; + return (a.line == b.line) && (a.front == b.front); + } + + // This compares a linedef side + public static bool operator !=(LinedefSide a, LinedefSide b) + { + if ((object.Equals(a, null)) && (object.Equals(b, null))) return false; + if ((!object.Equals(a, null)) && (object.Equals(b, null))) return true; + if ((object.Equals(a, null)) && (!object.Equals(b, null))) return true; + return (a.line != b.line) || (a.front != b.front); + } + + #endregion + } +} diff --git a/Source/Core/Geometry/LinedefsTracePath.cs b/Source/Core/Geometry/LinedefsTracePath.cs new file mode 100644 index 0000000..e56da46 --- /dev/null +++ b/Source/Core/Geometry/LinedefsTracePath.cs @@ -0,0 +1,134 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using CodeImp.DoomBuilder.Geometry; +using CodeImp.DoomBuilder.Rendering; +using SlimDX.Direct3D9; +using System.Drawing; +using CodeImp.DoomBuilder.Map; + +#endregion + +namespace CodeImp.DoomBuilder.Geometry +{ + public sealed class LinedefTracePath : List + { + #region ================== Constants + + #endregion + + #region ================== Variables + + #endregion + + #region ================== Properties + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + public LinedefTracePath() + { + // Initialize + } + + // Constructor + public LinedefTracePath(IEnumerable lines) : base(lines) + { + // Initialize + } + + // Constructor + public LinedefTracePath(ICollection lines) : base(lines.Count) + { + // Initialize + foreach (LinedefSide ls in lines) base.Add(ls.Line); + } + + // Constructor + public LinedefTracePath(LinedefTracePath p, Linedef add) : base(p) + { + // Initialize + base.Add(add); + } + + #endregion + + #region ================== Methods + + // This checks if the polygon is closed + public bool CheckIsClosed() + { + // There must be at least 2 sidedefs + if (base.Count > 1) + { + // The end sidedef must share a vertex with the first + return (base[0].Start == base[base.Count - 1].Start) || + (base[0].Start == base[base.Count - 1].End) || + (base[0].End == base[base.Count - 1].Start) || + (base[0].End == base[base.Count - 1].End); + } + else + { + // Not closed + return false; + } + } + + // This makes a polygon from the path + public EarClipPolygon MakePolygon(bool startfront) + { + EarClipPolygon p = new EarClipPolygon(); + bool forward = startfront; + + // Any sides at all? + if (base.Count > 0) + { + if (forward) + p.AddLast(new EarClipVertex(base[0].Start.Position, base[0].Front)); + else + p.AddLast(new EarClipVertex(base[0].End.Position, base[0].Back)); + + // Add all lines, but the first + for (int i = 1; i < base.Count; i++) + { + // Traverse direction changes? + if ((base[i - 1].Start == base[i].Start) || + (base[i - 1].End == base[i].End)) + forward = !forward; + + // Add next vertex + if (forward) + p.AddLast(new EarClipVertex(base[i].Start.Position, base[i].Front)); + else + p.AddLast(new EarClipVertex(base[i].End.Position, base[i].Back)); + } + } + + return p; + } + + #endregion + } +} diff --git a/Source/Core/Geometry/Plane.cs b/Source/Core/Geometry/Plane.cs new file mode 100644 index 0000000..be67c38 --- /dev/null +++ b/Source/Core/Geometry/Plane.cs @@ -0,0 +1,142 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; + +#endregion + +namespace CodeImp.DoomBuilder.Geometry +{ + public struct Plane + { + #region ================== Constants + + #endregion + + #region ================== Variables + + // + // Plane definition: + // A * x + B * y + C * z + D = 0 + // + // A, B, C is the normal + // D is the offset along the normal (negative) + // + private Vector3D normal; + private float offset; + + #endregion + + #region ================== Properties + + public Vector3D Normal { get { return normal; } } + public float Offset { get { return offset; } set { offset = value; } } + public float a { get { return normal.x; } } + public float b { get { return normal.y; } } + public float c { get { return normal.z; } } + public float d { get { return offset; } set { offset = value; } } + + #endregion + + #region ================== Constructors + + /// + public Plane(Vector3D normal, float offset) + { +#if DEBUG + if(!normal.IsNormalized()) + General.Fail("Attempt to create a plane with a vector that is not normalized!"); +#endif + this.normal = normal; + this.offset = offset; + } + + /// + public Plane(Vector3D normal, Vector3D position) + { +#if DEBUG + if(!normal.IsNormalized()) + General.Fail("Attempt to create a plane with a vector that is not normalized!"); +#endif + this.normal = normal; + this.offset = -Vector3D.DotProduct(normal, position); + } + + /// + public Plane(Vector3D p1, Vector3D p2, Vector3D p3) + { + this.normal = Vector3D.CrossProduct(p1 - p2, p3 - p2).GetNormal(); + this.offset = -Vector3D.DotProduct(normal, p2); + } + + #endregion + + #region ================== Methods + + /// + /// This tests for intersection using a position and direction + /// + public bool GetIntersection(Vector3D position, Vector3D direction, ref float u_ray) + { + float a = Vector3D.DotProduct(normal, direction); + if (a != 0.0f) + { + float b = Vector3D.DotProduct(normal, position); + u_ray = (offset - b) / a; + return true; + } + else + { + return false; + } + } + + /// + /// This returns the smallest distance to the plane and the side on which the point lies. + /// > 0 means the point lies on the front of the plane + /// < 0 means the point lies behind the plane + /// + public float Distance(Vector3D p) + { + return Vector3D.DotProduct(p, normal) + offset; + } + + /// + /// This returns a point on the plane closest to the given point + /// + public Vector3D ClosestOnPlane(Vector3D p) + { + float d = Vector3D.DotProduct(p, normal) + offset; + return p - normal * d; + } + + /// + /// This inverts the plane + /// + public Plane GetInverted() + { + return new Plane(-normal, -offset); + } + + #endregion + } +} diff --git a/Source/Core/Geometry/ProjectedFrustum2D.cs b/Source/Core/Geometry/ProjectedFrustum2D.cs new file mode 100644 index 0000000..5cada72 --- /dev/null +++ b/Source/Core/Geometry/ProjectedFrustum2D.cs @@ -0,0 +1,156 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using CodeImp.DoomBuilder.VisualModes; + +#endregion + +namespace CodeImp.DoomBuilder.Geometry +{ + public class ProjectedFrustum2D + { + #region ================== Variables + + // Frustum settings + private float near; + private float far; + private float fov; + private Vector2D pos; + private float xyangle; + private float zangle; + + // Frustum lines + private Line2D[] lines; + + // Circle + private Vector2D center; + private float radius; + + #endregion + + #region ================== Properties + + public float Near { get { return near; } } + public float Far { get { return far; } } + public float Fov { get { return fov; } } + public Vector2D Position { get { return pos; } } + public float XYAngle { get { return xyangle; } } + public float ZAngle { get { return zangle; } } + public Line2D[] Lines { get { return lines; } } + public Vector2D Center { get { return center; } } + public float Radius { get { return radius; } } + + #endregion + + #region ================== Constructor / Destructor + + // Constructor + public ProjectedFrustum2D(Vector2D pos, float xyangle, float zangle, float near, float far, float fov) + { + Vector2D[] forwards = new Vector2D[4]; + Vector2D[] downwards = new Vector2D[4]; + Vector2D[] corners = new Vector2D[4]; + + // Initialize + this.pos = pos; + this.xyangle = xyangle; + this.zangle = zangle; + this.near = near; + this.far = far; + this.fov = fov; + + // Make the corners for a forward frustum + // The corners are in this order: Left-Far, Right-Far, Left-Near, Right-Near + float fovhalf = fov * 0.5f; + float fovhalfcos = (float)Math.Cos(fovhalf); + float farsidelength = far / fovhalfcos; + float nearsidelength = near / fovhalfcos; + forwards[0] = pos + Vector2D.FromAngle(xyangle - fovhalf, farsidelength); + forwards[1] = pos + Vector2D.FromAngle(xyangle + fovhalf, farsidelength); + forwards[2] = pos + Vector2D.FromAngle(xyangle - fovhalf, nearsidelength); + forwards[3] = pos + Vector2D.FromAngle(xyangle + fovhalf, nearsidelength); + + // Make the corners for a downward frustum + // The corners are in the same order as above + //float farradius = far * (float)Math.Tan(fovhalf) * Angle2D.SQRT2; + float farradius = far * 0.5f * Angle2D.SQRT2; + downwards[0] = pos + Vector2D.FromAngle(xyangle - Angle2D.PI * 0.25f, farradius); + downwards[1] = pos + Vector2D.FromAngle(xyangle + Angle2D.PI * 0.25f, farradius); + downwards[2] = pos + Vector2D.FromAngle(xyangle - Angle2D.PI * 0.75f, farradius); + downwards[3] = pos + Vector2D.FromAngle(xyangle + Angle2D.PI * 0.75f, farradius); + + // Interpolate between the two to make the final corners depending on the z angle + float d = Math.Abs((float)Math.Sin(zangle)); + corners[0] = forwards[0] * (1.0f - d) + downwards[0] * d; + corners[1] = forwards[1] * (1.0f - d) + downwards[1] * d; + corners[2] = forwards[2] * (1.0f - d) + downwards[2] * d; + corners[3] = forwards[3] * (1.0f - d) + downwards[3] * d; + + // Make the frustum lines + // Note that the lines all have their right side inside the frustum! + lines = new Line2D[4]; + lines[0] = new Line2D(corners[2], corners[0]); + lines[1] = new Line2D(corners[1], corners[3]); + lines[2] = new Line2D(corners[3], corners[2]); + lines[3] = new Line2D(corners[0], corners[1]); + + // Calculate the circle center + center = (corners[0] + corners[1] + corners[2] + corners[3]) * 0.25f; + + // Calculate the radius from the center to the farthest corner + float radius2 = 0.0f; + for (int i = 0; i < corners.Length; i++) + { + float distance2 = Vector2D.DistanceSq(center, corners[i]); + if (distance2 > radius2) radius2 = distance2; + } + radius = (float)Math.Sqrt(radius2); + } + + #endregion + + #region ================== Methods + + // This checks if a specified circle is intersecting the frustum + // NOTE: This checks only against the actual frustum and does not use the frustum circle! + public bool IntersectCircle(Vector2D circlecenter, float circleradius) + { + // Go for all frustum lines + for (int i = 0; i < lines.Length; i++) + { + // Check on which side the circle center lies + if (lines[i].GetSideOfLine(circlecenter) < 0) + { + // Center is outside the frustum + // If the circle is not overlapping, it is not intersecting. + if (lines[i].GetDistanceToLineSq(circlecenter, false) > (circleradius * circleradius)) return false; + } + } + + // Intersecting! + return true; + } + + #endregion + } +} diff --git a/Source/Core/Geometry/SidedefAngleSorter.cs b/Source/Core/Geometry/SidedefAngleSorter.cs new file mode 100644 index 0000000..a9d472f --- /dev/null +++ b/Source/Core/Geometry/SidedefAngleSorter.cs @@ -0,0 +1,94 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using CodeImp.DoomBuilder.Map; + +#endregion + +namespace CodeImp.DoomBuilder.Geometry +{ + public sealed class SidedefAngleSorter : IComparer + { + // Variables + private Sidedef baseside; + private Vertex basevertex; + + // Constructor + public SidedefAngleSorter(Sidedef baseside, Vertex fromvertex) + { + // Initialize + this.baseside = baseside; + this.basevertex = fromvertex; + + // We have no destructor + GC.SuppressFinalize(this); + } + + // This calculates the relative angle between two sides + private float CalculateRelativeAngle(Sidedef a, Sidedef b) + { + float s, n, ana, anb; + Vector2D va, vb; + bool dir; + + // Determine angles + ana = a.Line.Angle; if (a.Line.End == basevertex) ana += Angle2D.PI; + anb = b.Line.Angle; if (b.Line.End == basevertex) anb += Angle2D.PI; + + // Take the difference from angles + n = Angle2D.Difference(ana, anb); + + // Get line end vertices a and b that are not connected to basevertex + if (a.Line.Start == basevertex) va = a.Line.End.Position; else va = a.Line.Start.Position; + if (b.Line.Start == basevertex) vb = b.Line.End.Position; else vb = b.Line.Start.Position; + + // Determine rotation direction + dir = baseside.IsFront; + if (baseside.Line.End == basevertex) dir = !dir; + + // Check to which side the angle goes and adjust angle as needed + s = Line2D.GetSideOfLine(va, vb, basevertex.Position); + if ((s < 0) && dir) n = Angle2D.PI2 - n; + if ((s > 0) && !dir) n = Angle2D.PI2 - n; + + // Return result + return n; + } + + // Comparer + public int Compare(Sidedef x, Sidedef y) + { + // Somehow, in a release build without debugger attached, + // the code above is not always the same when x == y... don't ask. + if (x == y) + return 0; + + // Calculate angles + float ax = CalculateRelativeAngle(baseside, x); + float ay = CalculateRelativeAngle(baseside, y); + + // Compare results + return Math.Sign(ay - ax); + } + } +} diff --git a/Source/Core/Geometry/SidedefsTracePath.cs b/Source/Core/Geometry/SidedefsTracePath.cs new file mode 100644 index 0000000..6d3fa99 --- /dev/null +++ b/Source/Core/Geometry/SidedefsTracePath.cs @@ -0,0 +1,110 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using CodeImp.DoomBuilder.Geometry; +using CodeImp.DoomBuilder.Rendering; +using SlimDX.Direct3D9; +using System.Drawing; +using CodeImp.DoomBuilder.Map; + +#endregion + +namespace CodeImp.DoomBuilder.Geometry +{ + public sealed class SidedefsTracePath : List + { + #region ================== Constants + + #endregion + + #region ================== Variables + + #endregion + + #region ================== Properties + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + public SidedefsTracePath() + { + // Initialize + } + + // Constructor + public SidedefsTracePath(SidedefsTracePath p, Sidedef add) : base(p) + { + // Initialize + base.Add(add); + } + + #endregion + + #region ================== Methods + + // This checks if the polygon is closed + public bool CheckIsClosed() + { + // There must be at least 2 sidedefs + if (base.Count > 1) + { + // The end sidedef must share a vertex with the first + return (base[0].Line.Start == base[base.Count - 1].Line.Start) || + (base[0].Line.Start == base[base.Count - 1].Line.End) || + (base[0].Line.End == base[base.Count - 1].Line.Start) || + (base[0].Line.End == base[base.Count - 1].Line.End); + } + else + { + // Not closed + return false; + } + } + + // This makes a polygon from the path + public EarClipPolygon MakePolygon() + { + EarClipPolygon p = new EarClipPolygon(); + + // Any sides at all? + if (base.Count > 0) + { + // Add all sides + for (int i = 0; i < base.Count; i++) + { + // On front or back? + if (base[i].IsFront) + p.AddLast(new EarClipVertex(base[i].Line.End.Position, base[i])); + else + p.AddLast(new EarClipVertex(base[i].Line.Start.Position, base[i])); + } + } + + return p; + } + + #endregion + } +} diff --git a/Source/Core/Geometry/Tools.cs b/Source/Core/Geometry/Tools.cs new file mode 100644 index 0000000..5346eb0 --- /dev/null +++ b/Source/Core/Geometry/Tools.cs @@ -0,0 +1,1805 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using CodeImp.DoomBuilder.Geometry; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Windows; +using SlimDX; +using SlimDX.Direct3D9; +using System.Drawing; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Data; +using System.Threading; +using CodeImp.DoomBuilder.Config; +using CodeImp.DoomBuilder.Types; + +#endregion + +namespace CodeImp.DoomBuilder.Geometry +{ + /// + /// Tools to work with geometry. + /// + public static class Tools + { + #region ================== Structures + + private struct SidedefSettings + { + public string newtexhigh; + public string newtexmid; + public string newtexlow; + } + + private struct SidedefAlignJob + { + public Sidedef sidedef; + + public int offsetx; + + // When this is true, the previous sidedef was on the left of + // this one and the texture X offset of this sidedef can be set + // directly. When this is false, the length of this sidedef + // must be subtracted from the X offset first. + public bool forward; + } + + private struct SidedefFillJob + { + public Sidedef sidedef; + + // Moving forward along the sidedef? + public bool forward; + } + + #endregion + + #region ================== Constants + + #endregion + + #region ================== Polygons and Triangles + + // Point inside the polygon? + // See: http://local.wasp.uwa.edu.au/~pbourke/geometry/insidepoly/ + public static bool PointInPolygon(ICollection polygon, Vector2D point) + { + Vector2D v1 = General.GetByIndex(polygon, polygon.Count - 1); + uint c = 0; + + // Go for all vertices + foreach (Vector2D v2 in polygon) + { + // Determine min/max values + float miny = Math.Min(v1.y, v2.y); + float maxy = Math.Max(v1.y, v2.y); + float maxx = Math.Max(v1.x, v2.x); + + // Check for intersection + if ((point.y > miny) && (point.y <= maxy)) + { + if (point.x <= maxx) + { + if (v1.y != v2.y) + { + float xint = (point.y - v1.y) * (v2.x - v1.x) / (v2.y - v1.y) + v1.x; + if ((v1.x == v2.x) || (point.x <= xint)) c++; + } + } + } + + // Move to next + v1 = v2; + } + + // Inside this polygon? + return (c & 0x00000001UL) != 0; + } + + #endregion + + #region ================== Pathfinding + + /// + /// This finds a potential sector at the given coordinates, + /// or returns null when a sector is not possible there. + /// + public static List FindPotentialSectorAt(Vector2D pos) + { + // Find the nearest line and determine side, then use the other method to create the sector + Linedef l = General.Map.Map.NearestLinedef(pos); + return FindPotentialSectorAt(l, (l.SideOfLine(pos) <= 0)); + } + + /// + /// This finds a potential sector starting at the given line and side, + /// or returns null when sector is not possible. + /// + public static List FindPotentialSectorAt(Linedef line, bool front) + { + List alllines = new List(); + + // Find the outer lines + EarClipPolygon p = FindOuterLines(line, front, alllines); + if (p != null) + { + // Find the inner lines + FindInnerLines(p, alllines); + return alllines; + } + else + return null; + } + + // This finds the inner lines of the sector and adds them to the sector polygon + private static void FindInnerLines(EarClipPolygon p, List alllines) + { + Vertex foundv; + bool vvalid, findmore; + Linedef foundline; + float foundangle = 0f; + bool foundlinefront; + RectangleF bbox = p.CreateBBox(); + + do + { + findmore = false; + + // Go for all vertices to find the right-most vertex inside the polygon + foundv = null; + foreach (Vertex v in General.Map.Map.Vertices) + { + // Inside the polygon bounding box? + if ((v.Position.x >= bbox.Left) && (v.Position.x <= bbox.Right) && + (v.Position.y >= bbox.Top) && (v.Position.y <= bbox.Bottom)) + { + // More to the right? + if ((foundv == null) || (v.Position.x >= foundv.Position.x)) + { + // Vertex is inside the polygon? + if (p.Intersect(v.Position)) + { + // Vertex has lines attached? + if (v.Linedefs.Count > 0) + { + // Go for all lines to see if the vertex is not of the polygon itsself + vvalid = true; + foreach (LinedefSide ls in alllines) + { + if ((ls.Line.Start == v) || (ls.Line.End == v)) + { + vvalid = false; + break; + } + } + + // Valid vertex? + if (vvalid) foundv = v; + } + } + } + } + } + + // Found a vertex inside the polygon? + if (foundv != null) + { + // Find the attached linedef with the smallest angle to the right + float targetangle = Angle2D.PIHALF; + foundline = null; + foreach (Linedef l in foundv.Linedefs) + { + // We need an angle unrelated to line direction, so correct for that + float lineangle = l.Angle; + if (l.End == foundv) lineangle += Angle2D.PI; + + // Better result? + float deltaangle = Angle2D.Difference(targetangle, lineangle); + if ((foundline == null) || (deltaangle < foundangle)) + { + foundline = l; + foundangle = deltaangle; + } + } + + // We already know that each linedef will go from this vertex + // to the left, because this is the right-most vertex in this area. + // If the line would go to the right, that means the other vertex of + // that line must lie outside this area and the mapper made an error. + // Should I check for this error and fail to create a sector in + // that case or ignore it and create a malformed sector (possibly + // breaking another sector also)? + + // Find the side at which to start pathfinding + Vector2D testpos = new Vector2D(100.0f, 0.0f); + foundlinefront = (foundline.SideOfLine(foundv.Position + testpos) < 0.0f); + + // Find inner path + List innerlines = FindClosestPath(foundline, foundlinefront, true); + if (innerlines != null) + { + // Make polygon + LinedefTracePath tracepath = new LinedefTracePath(innerlines); + EarClipPolygon innerpoly = tracepath.MakePolygon(true); + + // Check if the front of the line is outside the polygon + if (!innerpoly.Intersect(foundline.GetSidePoint(foundlinefront))) + { + // Valid hole found! + alllines.AddRange(innerlines); + p.InsertChild(innerpoly); + findmore = true; + } + } + } + } + // Continue until no more holes found + while (findmore); + } + + // This finds the outer lines of the sector as a polygon + // Returns null when no valid outer polygon can be found + private static EarClipPolygon FindOuterLines(Linedef line, bool front, List alllines) + { + Linedef scanline = line; + bool scanfront = front; + + do + { + // Find closest path + List pathlines = FindClosestPath(scanline, scanfront, true); + if (pathlines != null) + { + // Make polygon + LinedefTracePath tracepath = new LinedefTracePath(pathlines); + EarClipPolygon poly = tracepath.MakePolygon(true); + + // Check if the front of the line is inside the polygon + if (poly.Intersect(line.GetSidePoint(front))) + { + // Outer lines found! + alllines.AddRange(pathlines); + return poly; + } + else + { + // Inner lines found. This is not what we need, we want the outer lines. + // Find the right-most vertex to start a scan from there towards the outer lines. + Vertex foundv = null; + foreach (LinedefSide ls in pathlines) + { + if ((foundv == null) || (ls.Line.Start.Position.x > foundv.Position.x)) + foundv = ls.Line.Start; + + if ((foundv == null) || (ls.Line.End.Position.x > foundv.Position.x)) + foundv = ls.Line.End; + } + + // If foundv is null then something is horribly wrong with the + // path we received from FindClosestPath! + if (foundv == null) throw new Exception("FAIL!"); + + // From the right-most vertex trace outward to the right to + // find the next closest linedef, this is based on the idea that + // all sectors are closed. + Vector2D lineoffset = new Vector2D(100.0f, 0.0f); + Line2D testline = new Line2D(foundv.Position, foundv.Position + lineoffset); + scanline = null; + float foundu = float.MaxValue; + foreach (Linedef ld in General.Map.Map.Linedefs) + { + // Line to the right of start point? + if ((ld.Start.Position.x > foundv.Position.x) || + (ld.End.Position.x > foundv.Position.x)) + { + // Line intersecting the y axis? + if (!((ld.Start.Position.y > foundv.Position.y) && + (ld.End.Position.y > foundv.Position.y)) && + !((ld.Start.Position.y < foundv.Position.y) && + (ld.End.Position.y < foundv.Position.y))) + { + // Check if this linedef intersects our test line at a closer range + float thisu; + ld.Line.GetIntersection(testline, out thisu); + if ((thisu > 0.00001f) && (thisu < foundu) && !float.IsNaN(thisu)) + { + scanline = ld; + foundu = thisu; + } + } + } + } + + // Did we meet another line? + if (scanline != null) + { + // Determine on which side we should start the next pathfind + scanfront = (scanline.SideOfLine(foundv.Position) < 0.0f); + } + else + { + // Appearently we reached the end of the map, no sector possible here + return null; + } + } + } + else + { + // Can't find a path + return null; + } + } + while (true); + } + + /// + /// This finds the closest path from one vertex to another. + /// When turnatends is true, the algorithm will continue at the other side of the + /// line when a dead end has been reached. Returns null when no path could be found. + /// + //public static List FindClosestPath(Vertex start, float startangle, Vertex end, bool turnatends) + //{ + + //} + + /// + /// This finds the closest path from the beginning of a line to the end of the line. + /// When turnatends is true, the algorithm will continue at the other side of the + /// line when a dead end has been reached. Returns null when no path could be found. + /// + public static List FindClosestPath(Linedef startline, bool startfront, bool turnatends) + { + return FindClosestPath(startline, startfront, startline, startfront, turnatends); + } + + /// + /// This finds the closest path from the beginning of a line to the end of the line. + /// When turnatends is true, the algorithm will continue at the other side of the + /// line when a dead end has been reached. Returns null when no path could be found. + /// + public static List FindClosestPath(Linedef startline, bool startfront, Linedef endline, bool endfront, bool turnatends) + { + List path = new List(); + Dictionary tracecount = new Dictionary(); + Linedef nextline = startline; + bool nextfront = startfront; + + do + { + // Add line to path + path.Add(new LinedefSide(nextline, nextfront)); + if (!tracecount.ContainsKey(nextline)) tracecount.Add(nextline, 1); else tracecount[nextline]++; + + // Determine next vertex to use + Vertex v = nextfront ? nextline.End : nextline.Start; + + // Get list of linedefs and sort by angle + List lines = new List(v.Linedefs); + LinedefAngleSorter sorter = new LinedefAngleSorter(nextline, nextfront, v); + lines.Sort(sorter); + + // Source line is the only one? + if (lines.Count == 1) + { + // Are we allowed to trace along this line again? + if (turnatends && (!tracecount.ContainsKey(nextline) || (tracecount[nextline] < 3))) + { + // Turn around and go back along the other side of the line + nextfront = !nextfront; + } + else + { + // No more lines, trace ends here + path = null; + } + } + else + { + // Trace along the next line + Linedef prevline = nextline; + if (lines[0] == nextline) nextline = lines[1]; else nextline = lines[0]; + + // Are we allowed to trace this line again? + if (!tracecount.ContainsKey(nextline) || (tracecount[nextline] < 3)) + { + // Check if front side changes + if ((prevline.Start == nextline.Start) || + (prevline.End == nextline.End)) nextfront = !nextfront; + } + else + { + // No more lines, trace ends here + path = null; + } + } + } + // Continue as long as we have not reached the start yet + // or we have no next line to trace + while ((path != null) && ((nextline != endline) || (nextfront != endfront))); + + // If start and front are not the same, add the end to the list also + if ((path != null) && ((startline != endline) || (startfront != endfront))) + path.Add(new LinedefSide(endline, endfront)); + + // Return path (null when trace failed) + return path; + } + + #endregion + + #region ================== Sector Making + + // This makes the sector from the given lines and sides + // If nearbylines is not null, then this method will find the default + // properties from the nearest line in this collection when the + // default properties can't be found in the alllines collection. + // Return null when no new sector could be made. + public static Sector MakeSector(List alllines, List nearbylines) + { + Sector sourcesector = null; + SidedefSettings sourceside = new SidedefSettings(); + bool foundsidedefaults = false; + + if (General.Map.Map.Sectors.Count >= General.Map.FormatInterface.MaxSectors) + return null; + + Sector newsector = General.Map.Map.CreateSector(); + if (newsector == null) return null; + + // Check if any of the sides already has a sidedef + // Then we use information from that sidedef to make the others + foreach (LinedefSide ls in alllines) + { + if (ls.Front) + { + if (ls.Line.Front != null) + { + // Copy sidedef information if not already found + if (sourcesector == null) sourcesector = ls.Line.Front.Sector; + TakeSidedefSettings(ref sourceside, ls.Line.Front); + foundsidedefaults = true; + break; + } + } + else + { + if (ls.Line.Back != null) + { + // Copy sidedef information if not already found + if (sourcesector == null) sourcesector = ls.Line.Back.Sector; + TakeSidedefSettings(ref sourceside, ls.Line.Back); + foundsidedefaults = true; + break; + } + } + } + + // Now do the same for the other sides + // Note how information is only copied when not already found + // so this won't override information from the sides searched above + foreach (LinedefSide ls in alllines) + { + if (ls.Front) + { + if (ls.Line.Back != null) + { + // Copy sidedef information if not already found + if (sourcesector == null) sourcesector = ls.Line.Back.Sector; + TakeSidedefSettings(ref sourceside, ls.Line.Back); + foundsidedefaults = true; + break; + } + } + else + { + if (ls.Line.Front != null) + { + // Copy sidedef information if not already found + if (sourcesector == null) sourcesector = ls.Line.Front.Sector; + TakeSidedefSettings(ref sourceside, ls.Line.Front); + foundsidedefaults = true; + break; + } + } + } + + // Use default settings from neares linedef, if settings have been found yet + if ((nearbylines != null) && (alllines.Count > 0) && (!foundsidedefaults || (sourcesector == null))) + { + Vector2D testpoint = alllines[0].Line.GetSidePoint(alllines[0].Front); + Linedef nearest = MapSet.NearestLinedef(nearbylines, testpoint); + if (nearest != null) + { + Sidedef defaultside; + float side = nearest.SideOfLine(testpoint); + if (side < 0.0f) + defaultside = nearest.Front; + else + defaultside = nearest.Back; + + if (defaultside != null) + { + if (sourcesector == null) sourcesector = defaultside.Sector; + TakeSidedefSettings(ref sourceside, defaultside); + } + } + } + + // Use defaults where no settings could be found + TakeSidedefDefaults(ref sourceside); + + // Found a source sector? + if (sourcesector != null) + { + // Copy properties from source to new sector + sourcesector.CopyPropertiesTo(newsector); + } + else + { + // No source sector, apply default sector properties + ApplyDefaultsToSector(newsector); + } + + // Go for all sides to make sidedefs + foreach (LinedefSide ls in alllines) + { + // We may only remove a useless middle texture when + // the line was previously singlesided + bool wassinglesided = (ls.Line.Back == null) || (ls.Line.Front == null); + + if (ls.Front) + { + // Create sidedef is needed and ensure it points to the new sector + if (ls.Line.Front == null) General.Map.Map.CreateSidedef(ls.Line, true, newsector); + if (ls.Line.Front == null) return null; + if (ls.Line.Front.Sector != newsector) ls.Line.Front.SetSector(newsector); + ApplyDefaultsToSidedef(ls.Line.Front, sourceside); + } + else + { + // Create sidedef is needed and ensure it points to the new sector + if (ls.Line.Back == null) General.Map.Map.CreateSidedef(ls.Line, false, newsector); + if (ls.Line.Back == null) return null; + if (ls.Line.Back.Sector != newsector) ls.Line.Back.SetSector(newsector); + ApplyDefaultsToSidedef(ls.Line.Back, sourceside); + } + + // Update line + if (ls.Line.Front != null) ls.Line.Front.RemoveUnneededTextures(wassinglesided); + if (ls.Line.Back != null) ls.Line.Back.RemoveUnneededTextures(wassinglesided); + + // Apply single/double sided flags if the double-sided-ness changed + if ((wassinglesided && ((ls.Line.Front != null) && (ls.Line.Back != null))) || + (!wassinglesided && ((ls.Line.Front == null) || (ls.Line.Back == null)))) + ls.Line.ApplySidedFlags(); + } + + // Return the new sector + return newsector; + } + + + // This joins a sector with the given lines and sides. Returns null when operation could not be completed. + public static Sector JoinSector(List alllines, Sidedef original) + { + SidedefSettings sourceside = new SidedefSettings(); + + // Take settings fro mthe original side + TakeSidedefSettings(ref sourceside, original); + + // Use defaults where no settings could be found + TakeSidedefDefaults(ref sourceside); + + // Go for all sides to make sidedefs + foreach (LinedefSide ls in alllines) + { + if (ls.Front) + { + // Create sidedef if needed + if (ls.Line.Front == null) + { + Sidedef sd = General.Map.Map.CreateSidedef(ls.Line, true, original.Sector); + if (sd == null) return null; + ApplyDefaultsToSidedef(ls.Line.Front, sourceside); + ls.Line.ApplySidedFlags(); + + // We must remove the (now useless) middle texture on the other side + if (ls.Line.Back != null) ls.Line.Back.RemoveUnneededTextures(true, true); + } + // Added 23-9-08, can we do this or will it break things? + else + { + // Link to the new sector + ls.Line.Front.SetSector(original.Sector); + } + } + else + { + // Create sidedef if needed + if (ls.Line.Back == null) + { + Sidedef sd = General.Map.Map.CreateSidedef(ls.Line, false, original.Sector); + if (sd == null) return null; + ApplyDefaultsToSidedef(ls.Line.Back, sourceside); + ls.Line.ApplySidedFlags(); + + // We must remove the (now useless) middle texture on the other side + if (ls.Line.Front != null) ls.Line.Front.RemoveUnneededTextures(true, true); + } + // Added 23-9-08, can we do this or will it break things? + else + { + // Link to the new sector + ls.Line.Back.SetSector(original.Sector); + } + } + } + + // Return the new sector + return original.Sector; + } + + // This takes default settings if not taken yet + private static void TakeSidedefDefaults(ref SidedefSettings settings) + { + // Use defaults where no settings could be found + if (settings.newtexhigh == null) settings.newtexhigh = General.Settings.DefaultTexture; + if (settings.newtexmid == null) settings.newtexmid = General.Settings.DefaultTexture; + if (settings.newtexlow == null) settings.newtexlow = General.Settings.DefaultTexture; + } + + // This takes sidedef settings if not taken yet + private static void TakeSidedefSettings(ref SidedefSettings settings, Sidedef side) + { + if ((side.LongHighTexture != MapSet.EmptyLongName) && (settings.newtexhigh == null)) + settings.newtexhigh = side.HighTexture; + if ((side.LongMiddleTexture != MapSet.EmptyLongName) && (settings.newtexmid == null)) + settings.newtexmid = side.MiddleTexture; + if ((side.LongLowTexture != MapSet.EmptyLongName) && (settings.newtexlow == null)) + settings.newtexlow = side.LowTexture; + } + + // This applies defaults to a sidedef + private static void ApplyDefaultsToSidedef(Sidedef sd, SidedefSettings defaults) + { + if (sd.HighRequired() && sd.HighTexture.StartsWith("-")) sd.SetTextureHigh(defaults.newtexhigh); + if (sd.MiddleRequired() && sd.MiddleTexture.StartsWith("-")) sd.SetTextureMid(defaults.newtexmid); + if (sd.LowRequired() && sd.LowTexture.StartsWith("-")) sd.SetTextureLow(defaults.newtexlow); + } + + // This applies defaults to a sector + private static void ApplyDefaultsToSector(Sector s) + { + s.SetFloorTexture(General.Settings.DefaultFloorTexture); + s.SetCeilTexture(General.Settings.DefaultCeilingTexture); + s.FloorHeight = General.Settings.DefaultFloorHeight; + s.CeilHeight = General.Settings.DefaultCeilingHeight; + s.Brightness = General.Settings.DefaultBrightness; + } + + #endregion + + #region ================== Sector Labels + + // This finds the ideal label positions for a sector + public static List FindLabelPositions(Sector s) + { + List positions = new List(2); + int islandoffset = 0; + + // Do we have a triangulation? + Triangulation triangles = s.Triangles; + if (triangles != null) + { + // Go for all islands + for (int i = 0; i < triangles.IslandVertices.Count; i++) + { + Dictionary sides = new Dictionary(triangles.IslandVertices[i] >> 1); + List candidatepositions = new List(triangles.IslandVertices[i] >> 1); + float founddistance = float.MinValue; + Vector2D foundposition = new Vector2D(); + float minx = float.MaxValue; + float miny = float.MaxValue; + float maxx = float.MinValue; + float maxy = float.MinValue; + + // Make candidate lines that are not along sidedefs + // We do this before testing the candidate against the sidedefs so that + // we can collect the relevant sidedefs first in the same run + for (int t = 0; t < triangles.IslandVertices[i]; t += 3) + { + int triangleoffset = islandoffset + t; + Vector2D v1 = triangles.Vertices[triangleoffset + 2]; + Sidedef sd = triangles.Sidedefs[triangleoffset + 2]; + for (int v = 0; v < 3; v++) + { + Vector2D v2 = triangles.Vertices[triangleoffset + v]; + + // Not along a sidedef? Then this line is across the sector + // and guaranteed to be inside the sector! + if (sd == null) + { + // Make the line + candidatepositions.Add(v1 + (v2 - v1) * 0.5f); + } + else + { + // This sidedefs is part of this island and must be checked + // so add it to the dictionary + sides[sd] = sd.Line; + } + + // Make bbox of this island + minx = Math.Min(minx, v1.x); + miny = Math.Min(miny, v1.y); + maxx = Math.Max(maxx, v1.x); + maxy = Math.Max(maxy, v1.y); + + // Next + sd = triangles.Sidedefs[triangleoffset + v]; + v1 = v2; + } + } + + // Any candidate lines found at all? + if (candidatepositions.Count > 0) + { + // Start with the first line + foreach (Vector2D candidatepos in candidatepositions) + { + // Check distance against other lines + float smallestdist = int.MaxValue; + foreach (KeyValuePair sd in sides) + { + // Check the distance + float distance = sd.Value.DistanceToSq(candidatepos, true); + smallestdist = Math.Min(smallestdist, distance); + } + + // Keep this candidate if it is better than previous + if (smallestdist > founddistance) + { + foundposition = candidatepos; + founddistance = smallestdist; + } + } + + // No cceptable line found, just use the first! + positions.Add(new LabelPositionInfo(foundposition, (float)Math.Sqrt(founddistance))); + } + else + { + // No candidate lines found. + + // Check to see if the island is a triangle + if (triangles.IslandVertices[i] == 3) + { + // Use the center of the triangle + // TODO: Use the 'incenter' instead, see http://mathworld.wolfram.com/Incenter.html + Vector2D v = (triangles.Vertices[islandoffset] + triangles.Vertices[islandoffset + 1] + triangles.Vertices[islandoffset + 2]) / 3.0f; + float d = Line2D.GetDistanceToLineSq(triangles.Vertices[islandoffset], triangles.Vertices[islandoffset + 1], v, false); + d = Math.Min(d, Line2D.GetDistanceToLineSq(triangles.Vertices[islandoffset + 1], triangles.Vertices[islandoffset + 2], v, false)); + d = Math.Min(d, Line2D.GetDistanceToLineSq(triangles.Vertices[islandoffset + 2], triangles.Vertices[islandoffset], v, false)); + positions.Add(new LabelPositionInfo(v, (float)Math.Sqrt(d))); + } + else + { + // Use the center of this island. + float d = Math.Min((maxx - minx) * 0.5f, (maxy - miny) * 0.5f); + positions.Add(new LabelPositionInfo(new Vector2D(minx + (maxx - minx) * 0.5f, miny + (maxy - miny) * 0.5f), d)); + } + } + + // Done with this island + islandoffset += triangles.IslandVertices[i]; + } + } + else + { + // No triangulation was made. FAIL! + General.Fail("No triangulation exists for sector " + s + " Triangulation is required to create label positions for a sector."); + } + + // Done + return positions; + } + + #endregion + + #region ================== Drawing + + /// + /// This draws lines with the given points. Note that this tool removes any existing geometry + /// marks and marks the new lines and vertices when done. Also marks the sectors that were added. + /// Returns false when the drawing failed. + /// + public static bool DrawLines(IList points) + { + List newverts = new List(); + List intersectverts = new List(); + List newlines = new List(); + List newlinescw = new List(); + List oldlines = new List(General.Map.Map.Linedefs); + List insidesides = new List(); + List mergeverts = new List(); + List nonmergeverts = new List(General.Map.Map.Vertices); + MapSet map = General.Map.Map; + + General.Map.Map.ClearAllMarks(false); + + // Any points to do? + if (points.Count > 0) + { + /***************************************************\ + Create the drawing + \***************************************************/ + + // Make first vertex + Vertex v1 = map.CreateVertex(points[0].pos); + if (v1 == null) return false; + v1.Marked = true; + + // Keep references + newverts.Add(v1); + if (points[0].stitch) mergeverts.Add(v1); else nonmergeverts.Add(v1); + + // Go for all other points + for (int i = 1; i < points.Count; i++) + { + // Create vertex for point + Vertex v2 = map.CreateVertex(points[i].pos); + if (v2 == null) return false; + v2.Marked = true; + + // Keep references + newverts.Add(v2); + if (points[i].stitch) mergeverts.Add(v2); else nonmergeverts.Add(v2); + + // Create line between point and previous + Linedef ld = map.CreateLinedef(v1, v2); + if (ld == null) return false; + ld.Marked = true; + ld.ApplySidedFlags(); + ld.SetFlag("8388608", true); // villsa 9/11/11 (builder64) + ld.UpdateCache(); + newlines.Add(ld); + + // Should we split this line to merge with intersecting lines? + if (points[i - 1].stitchline && points[i].stitchline) + { + // Check if any other lines intersect this line + List intersections = new List(); + Line2D measureline = ld.Line; + foreach (Linedef ld2 in map.Linedefs) + { + // Intersecting? + // We only keep the unit length from the start of the line and + // do the real splitting later, when all intersections are known + float u; + if (ld2.Line.GetIntersection(measureline, out u)) + { + if (!float.IsNaN(u) && (u > 0.0f) && (u < 1.0f) && (ld2 != ld)) + intersections.Add(u); + } + } + + // Sort the intersections + intersections.Sort(); + + // Go for all found intersections + Linedef splitline = ld; + foreach (float u in intersections) + { + // Calculate exact coordinates where to split + // We use measureline for this, because the original line + // may already have changed in length due to a previous split + Vector2D splitpoint = measureline.GetCoordinatesAt(u); + + // Make the vertex + Vertex splitvertex = map.CreateVertex(splitpoint); + if (splitvertex == null) return false; + splitvertex.Marked = true; + newverts.Add(splitvertex); + mergeverts.Add(splitvertex); // <-- add to merge? + intersectverts.Add(splitvertex); + + // The Split method ties the end of the original line to the given + // vertex and starts a new line at the given vertex, so continue + // splitting with the new line, because the intersections are sorted + // from low to high (beginning at the original line start) + splitline = splitline.Split(splitvertex); + if (splitline == null) return false; + splitline.ApplySidedFlags(); + newlines.Add(splitline); + } + } + + // Next + v1 = v2; + } + + // Join merge vertices so that overlapping vertices in the draw become one. + map.BeginAddRemove(); + MapSet.JoinVertices(mergeverts, mergeverts, false, MapSet.STITCH_DISTANCE); + map.EndAddRemove(); + + /***************************************************\ + Find a way to close the drawing + \***************************************************/ + + // We prefer a closed polygon, because then we can determine the interior properly + // Check if the two ends of the polygon are closed + bool drawingclosed = false; + bool splittingonly = false; + if (newlines.Count > 0) + { + Linedef firstline = newlines[0]; + Linedef lastline = newlines[newlines.Count - 1]; + drawingclosed = (firstline.Start == lastline.End); + if (!drawingclosed) + { + // When not closed, we will try to find a path to close it. + // But first we check if any of our new lines are inside existing sectors, because + // if they are then we are splitting sectors and cannot accurately find a closed path + // to close our polygon. In that case, we want to do sector splits only. + foreach (Linedef ld in newlines) + { + Vector2D ldcp = ld.GetCenterPoint(); + Linedef nld = MapSet.NearestLinedef(oldlines, ldcp); + if (nld != null) + { + float ldside = nld.SideOfLine(ldcp); + if (ldside < 0.0f) + { + if (nld.Front != null) + { + splittingonly = true; + break; + } + } + else if (ldside > 0.0f) + { + if (nld.Back != null) + { + splittingonly = true; + break; + } + } + else + { + // We can't tell, so lets ignore this for now. + } + } + } + + // Not splitting only? + if (!splittingonly) + { + // First and last vertex stitch with geometry? + if (points[0].stitch && points[points.Count - 1].stitch) + { + List startpoints = new List(); + List endpoints = new List(); + + // Find out where the start will stitch and create test points + Linedef l1 = MapSet.NearestLinedefRange(oldlines, firstline.Start.Position, MapSet.STITCH_DISTANCE); + Vertex vv1 = null; + if (l1 != null) + { + startpoints.Add(new LinedefSide(l1, true)); + startpoints.Add(new LinedefSide(l1, false)); + } + else + { + // Not stitched with a linedef, so check if it will stitch with a vertex + vv1 = MapSet.NearestVertexSquareRange(nonmergeverts, firstline.Start.Position, MapSet.STITCH_DISTANCE); + if ((vv1 != null) && (vv1.Linedefs.Count > 0)) + { + // Now we take the two linedefs with adjacent angles to the drawn line + List lines = new List(vv1.Linedefs); + lines.Sort(new LinedefAngleSorter(firstline, true, firstline.Start)); + startpoints.Add(new LinedefSide(lines[0], true)); + startpoints.Add(new LinedefSide(lines[0], false)); + lines.Sort(new LinedefAngleSorter(firstline, false, firstline.Start)); + startpoints.Add(new LinedefSide(lines[0], true)); + startpoints.Add(new LinedefSide(lines[0], false)); + } + } + + // Find out where the end will stitch and create test points + Linedef l2 = MapSet.NearestLinedefRange(oldlines, lastline.End.Position, MapSet.STITCH_DISTANCE); + Vertex vv2 = null; + if (l2 != null) + { + endpoints.Add(new LinedefSide(l2, true)); + endpoints.Add(new LinedefSide(l2, false)); + } + else + { + // Not stitched with a linedef, so check if it will stitch with a vertex + vv2 = MapSet.NearestVertexSquareRange(nonmergeverts, lastline.End.Position, MapSet.STITCH_DISTANCE); + if ((vv2 != null) && (vv2.Linedefs.Count > 0)) + { + // Now we take the two linedefs with adjacent angles to the drawn line + List lines = new List(vv2.Linedefs); + lines.Sort(new LinedefAngleSorter(firstline, true, lastline.End)); + endpoints.Add(new LinedefSide(lines[0], true)); + endpoints.Add(new LinedefSide(lines[0], false)); + lines.Sort(new LinedefAngleSorter(firstline, false, lastline.End)); + endpoints.Add(new LinedefSide(lines[0], true)); + endpoints.Add(new LinedefSide(lines[0], false)); + } + } + + // Found any start and end points? + if ((startpoints.Count > 0) && (endpoints.Count > 0)) + { + List shortestpath = null; + + // Both stitched to the same line? + if ((l1 == l2) && (l1 != null)) + { + // Then just connect the two + shortestpath = new List(); + shortestpath.Add(new LinedefSide(l1, true)); + } + // One stitched to a line and the other to a vertex of that line? + else if ((l1 != null) && (vv2 != null) && ((l1.Start == vv2) || (l1.End == vv2))) + { + // Then just connect the two + shortestpath = new List(); + shortestpath.Add(new LinedefSide(l1, true)); + } + // The other stitched to a line and the first to a vertex of that line? + else if ((l2 != null) && (vv1 != null) && ((l2.Start == vv1) || (l2.End == vv1))) + { + // Then just connect the two + shortestpath = new List(); + shortestpath.Add(new LinedefSide(l2, true)); + } + else + { + // Find the shortest, closest path between start and end points + foreach (LinedefSide startp in startpoints) + { + foreach (LinedefSide endp in endpoints) + { + List p; + p = Tools.FindClosestPath(startp.Line, startp.Front, endp.Line, endp.Front, true); + if ((p != null) && ((shortestpath == null) || (p.Count < shortestpath.Count))) shortestpath = p; + p = Tools.FindClosestPath(endp.Line, endp.Front, startp.Line, startp.Front, true); + if ((p != null) && ((shortestpath == null) || (p.Count < shortestpath.Count))) shortestpath = p; + } + } + } + + // Found a path? + if (shortestpath != null) + { + // Check which direction the path goes in + bool pathforward = false; + foreach (LinedefSide startp in startpoints) + { + if (shortestpath[0].Line == startp.Line) + { + pathforward = true; + break; + } + } + + // TEST + /* + General.Map.Renderer2D.StartOverlay(true); + foreach(LinedefSide lsd in shortestpath) + { + General.Map.Renderer2D.RenderLine(lsd.Line.Start.Position, lsd.Line.End.Position, 2, new PixelColor(255, 0, 255, 0), true); + } + General.Map.Renderer2D.Finish(); + General.Map.Renderer2D.Present(); + Thread.Sleep(1000); + */ + + // Begin at first vertex in path + if (pathforward) + v1 = firstline.Start; + else + v1 = lastline.End; + + // Go for all vertices in the path to make additional lines + for (int i = 1; i < shortestpath.Count; i++) + { + // Get the next position + Vector2D v2pos = shortestpath[i].Front ? shortestpath[i].Line.Start.Position : shortestpath[i].Line.End.Position; + + // Make the new vertex + Vertex v2 = map.CreateVertex(v2pos); + if (v2 == null) return false; + v2.Marked = true; + mergeverts.Add(v2); + + // Make the line + Linedef ld = map.CreateLinedef(v1, v2); + if (ld == null) return false; + ld.Marked = true; + ld.ApplySidedFlags(); + ld.UpdateCache(); + newlines.Add(ld); + + // Next + v1 = v2; + } + + // Make the final line + Linedef lld; + if (pathforward) + lld = map.CreateLinedef(v1, lastline.End); + else + lld = map.CreateLinedef(v1, firstline.Start); + + if (lld == null) return false; + + // Setup line + lld.Marked = true; + lld.ApplySidedFlags(); + lld.UpdateCache(); + newlines.Add(lld); + + // Drawing is now closed + drawingclosed = true; + + // Join merge vertices so that overlapping vertices in the draw become one. + MapSet.JoinVertices(mergeverts, mergeverts, false, MapSet.STITCH_DISTANCE); + } + } + } + } + } + } + + // Merge intersetion vertices with the new lines. This completes the + // self intersections for which splits were made above. + map.Update(true, false); + map.BeginAddRemove(); + MapSet.SplitLinesByVertices(newlines, intersectverts, MapSet.STITCH_DISTANCE, null); + MapSet.SplitLinesByVertices(newlines, mergeverts, MapSet.STITCH_DISTANCE, null); + map.EndAddRemove(); + + /***************************************************\ + Determine drawing interior + \***************************************************/ + + // In step 3 we will make sectors on the interior sides and join sectors on the + // exterior sides, but because the user could have drawn counterclockwise or just + // some weird polygon. The following code figures out the interior side of all + // new lines. + map.Update(true, false); + foreach (Linedef ld in newlines) + { + // Find closest path starting with the front of this linedef + List pathlines = Tools.FindClosestPath(ld, true, true); + if (pathlines != null) + { + // Make polygon + LinedefTracePath tracepath = new LinedefTracePath(pathlines); + EarClipPolygon pathpoly = tracepath.MakePolygon(true); + + // Check if the front of the line is outside the polygon + if ((pathpoly.CalculateArea() > 0.001f) && !pathpoly.Intersect(ld.GetSidePoint(true))) + { + // Now trace from the back side of the line to see if + // the back side lies in the interior. I don't want to + // flip the line if it is not helping. + + // Find closest path starting with the back of this linedef + pathlines = Tools.FindClosestPath(ld, false, true); + if (pathlines != null) + { + // Make polygon + tracepath = new LinedefTracePath(pathlines); + pathpoly = tracepath.MakePolygon(true); + + // Check if the front of the line is inside the polygon + ld.FrontInterior = (pathpoly.CalculateArea() < 0.001f) || pathpoly.Intersect(ld.GetSidePoint(true)); + } + else + { + ld.FrontInterior = true; + } + } + else + { + ld.FrontInterior = true; + } + } + else + { + ld.FrontInterior = true; + } + } + + /***************************************************\ + Merge the new geometry + \***************************************************/ + + // Mark only the vertices that should be merged + map.ClearMarkedVertices(false); + foreach (Vertex v in mergeverts) v.Marked = true; + + // Before this point, the new geometry is not linked with the existing geometry. + // Now perform standard geometry stitching to merge the new geometry with the rest + // of the map. The marked vertices indicate the new geometry. + map.StitchGeometry(); + map.Update(true, false); + + // Find our new lines again, because they have been merged with the other geometry + // but their Marked property is copied where they have joined. + newlines = map.GetMarkedLinedefs(true); + + // Remove any disposed old lines + List prevoldlines = oldlines; + oldlines = new List(prevoldlines.Count); + foreach (Linedef ld in prevoldlines) + if (!ld.IsDisposed) oldlines.Add(ld); + + /***************************************************\ + Join and create new sectors + \***************************************************/ + + // The code below atempts to create sectors on the interior sides of the drawn + // geometry and joins sectors on the other sides of the drawn geometry. + // This code does not change any geometry, it only makes/updates sidedefs. + bool sidescreated = false; + bool[] frontsdone = new bool[newlines.Count]; + bool[] backsdone = new bool[newlines.Count]; + for (int i = 0; i < newlines.Count; i++) + { + Linedef ld = newlines[i]; + + // Interior not done yet? + if ((ld.FrontInterior && !frontsdone[i]) || (!ld.FrontInterior && !backsdone[i])) + { + // Find a way to create a sector here + List sectorlines = Tools.FindPotentialSectorAt(ld, ld.FrontInterior); + if (sectorlines != null) + { + sidescreated = true; + + // When none of the linedef sides exist yet, this is a true new + // sector that will be created out of the void! + bool istruenewsector = true; + foreach (LinedefSide ls in sectorlines) + { + if ((ls.Front && (ls.Line.Front != null)) || + (!ls.Front && (ls.Line.Back != null))) + { + istruenewsector = false; + break; + } + } + + // But we don't want to create sectors out of the void when we + // decided that we only want to split sectors. + if (!istruenewsector || !splittingonly) + { + // Make the new sector + Sector newsector = Tools.MakeSector(sectorlines, oldlines); + if (newsector == null) return false; + + if (istruenewsector) newsector.Marked = true; + + // Go for all sidedefs in this new sector + foreach (Sidedef sd in newsector.Sidedefs) + { + // Keep list of sides inside created sectors + insidesides.Add(sd); + + // Side matches with a side of our new lines? + int lineindex = newlines.IndexOf(sd.Line); + if (lineindex > -1) + { + // Mark this side as done + if (sd.IsFront) + frontsdone[lineindex] = true; + else + backsdone[lineindex] = true; + } + } + } + } + } + + // Exterior not done yet? + if ((ld.FrontInterior && !backsdone[i]) || (!ld.FrontInterior && !frontsdone[i])) + { + // Find a way to create a sector here + List sectorlines = Tools.FindPotentialSectorAt(ld, !ld.FrontInterior); + if (sectorlines != null) + { + // Check if any of the surrounding lines originally have sidedefs we can join + Sidedef joinsidedef = null; + foreach (LinedefSide ls in sectorlines) + { + if (ls.Front && (ls.Line.Front != null)) + { + joinsidedef = ls.Line.Front; + break; + } + else if (!ls.Front && (ls.Line.Back != null)) + { + joinsidedef = ls.Line.Back; + break; + } + } + + // Join? + if (joinsidedef != null) + { + sidescreated = true; + + // We only want to modify our new lines when joining a sector + // (or it may break nearby self-referencing sectors) + List newsectorlines = new List(sectorlines.Count); + foreach (LinedefSide sd in sectorlines) + { + // Side matches with a side of our new lines? + int lineindex = newlines.IndexOf(sd.Line); + if (lineindex > -1) + { + // Add to list + newsectorlines.Add(sd); + + // Mark this side as done + if (sd.Front) + frontsdone[lineindex] = true; + else + backsdone[lineindex] = true; + } + } + + // Have our new lines join the existing sector + if (Tools.JoinSector(newsectorlines, joinsidedef) == null) + return false; + } + } + } + } + + /***************************************************\ + Corrections and clean up + \***************************************************/ + + // Make corrections for backward linedefs + MapSet.FlipBackwardLinedefs(newlines); + + // Check if any of our new lines have sides + if (sidescreated) + { + // Then remove the lines which have no sides at all + for (int i = newlines.Count - 1; i >= 0; i--) + { + // Remove the line if it has no sides + if ((newlines[i].Front == null) && (newlines[i].Back == null)) newlines[i].Dispose(); + } + } + + // Mark new geometry only + General.Map.Map.ClearMarkedLinedefs(false); + General.Map.Map.ClearMarkedVertices(false); + foreach (Vertex v in newverts) v.Marked = true; + foreach (Linedef l in newlines) l.Marked = true; + } + + return true; + } + + #endregion + + #region ================== Flat Floodfill + + // This performs flat floodfill over sector floors or ceilings that match with the same flat + // NOTE: This method uses the sectors marking to indicate which sides have been filled + // When resetsectormarks is set to true, all sectors will first be marked false (not aligned). + // Setting resetsectormarks to false is usefull to fill only within a specific selection + // (set the marked property to true for the sectors outside the selection) + public static void FloodfillFlats(Sector start, bool fillceilings, long originalflat, ImageData fillflat, bool resetsectormarks) + { + Stack todo = new Stack(50); + + // Mark all sectors false (they will be marked true when the flat is modified) + if (resetsectormarks) General.Map.Map.ClearMarkedSectors(false); + + // Begin with first sector + if (((start.LongFloorTexture == originalflat) && !fillceilings) || + ((start.LongCeilTexture == originalflat) && fillceilings)) + { + todo.Push(start); + } + + // Continue until nothing more to align + while (todo.Count > 0) + { + // Get the sector to do + Sector s = todo.Pop(); + + // Apply new flat + if (fillceilings) + s.SetCeilTexture(fillflat.Name); + else + s.SetFloorTexture(fillflat.Name); + s.Marked = true; + + // Go for all sidedefs to add neighbouring sectors + foreach (Sidedef sd in s.Sidedefs) + { + // Sector on the other side of the line that we haven't checked yet? + if ((sd.Other != null) && !sd.Other.Sector.Marked) + { + Sector os = sd.Other.Sector; + + // Check if texture matches + if (((os.LongFloorTexture == originalflat) && !fillceilings) || + ((os.LongCeilTexture == originalflat) && fillceilings)) + { + todo.Push(os); + } + } + } + } + } + + #endregion + + #region ================== Texture Floodfill + + // This performs texture floodfill along all walls that match with the same texture + // NOTE: This method uses the sidedefs marking to indicate which sides have been filled + // When resetsidemarks is set to true, all sidedefs will first be marked false (not aligned). + // Setting resetsidemarks to false is usefull to fill only within a specific selection + // (set the marked property to true for the sidedefs outside the selection) + public static void FloodfillTextures(Sidedef start, long originaltexture, ImageData filltexture, bool resetsidemarks) + { + Stack todo = new Stack(50); + + // Mark all sidedefs false (they will be marked true when the texture is aligned) + if (resetsidemarks) General.Map.Map.ClearMarkedSidedefs(false); + + // Begin with first sidedef + if (SidedefTextureMatch(start, originaltexture)) + { + SidedefFillJob first = new SidedefFillJob(); + first.sidedef = start; + first.forward = true; + todo.Push(first); + } + + // Continue until nothing more to align + while (todo.Count > 0) + { + // Get the align job to do + SidedefFillJob j = todo.Pop(); + + // Apply texturing + if (j.sidedef.LongHighTexture == originaltexture) j.sidedef.SetTextureHigh(filltexture.Name); + if ((((j.sidedef.MiddleTexture.Length > 0) && (j.sidedef.MiddleTexture[0] != '-')) || j.sidedef.MiddleRequired()) && + (j.sidedef.LongMiddleTexture == originaltexture)) j.sidedef.SetTextureMid(filltexture.Name); + if (j.sidedef.LongLowTexture == originaltexture) j.sidedef.SetTextureLow(filltexture.Name); + j.sidedef.Marked = true; + + if (j.forward) + { + Vertex v; + + // Add sidedefs forward (connected to the right vertex) + v = j.sidedef.IsFront ? j.sidedef.Line.End : j.sidedef.Line.Start; + AddSidedefsForFloodfill(todo, v, true, originaltexture); + + // Add sidedefs backward (connected to the left vertex) + v = j.sidedef.IsFront ? j.sidedef.Line.Start : j.sidedef.Line.End; + AddSidedefsForFloodfill(todo, v, false, originaltexture); + } + else + { + Vertex v; + + // Add sidedefs backward (connected to the left vertex) + v = j.sidedef.IsFront ? j.sidedef.Line.Start : j.sidedef.Line.End; + AddSidedefsForFloodfill(todo, v, false, originaltexture); + + // Add sidedefs forward (connected to the right vertex) + v = j.sidedef.IsFront ? j.sidedef.Line.End : j.sidedef.Line.Start; + AddSidedefsForFloodfill(todo, v, true, originaltexture); + } + } + } + + // This adds the matching, unmarked sidedefs from a vertex for texture alignment + private static void AddSidedefsForFloodfill(Stack stack, Vertex v, bool forward, long texturelongname) + { + foreach (Linedef ld in v.Linedefs) + { + Sidedef side1 = forward ? ld.Front : ld.Back; + Sidedef side2 = forward ? ld.Back : ld.Front; + if ((ld.Start == v) && (side1 != null) && !side1.Marked) + { + if (SidedefTextureMatch(side1, texturelongname)) + { + SidedefFillJob nj = new SidedefFillJob(); + nj.forward = forward; + nj.sidedef = side1; + stack.Push(nj); + } + } + else if ((ld.End == v) && (side2 != null) && !side2.Marked) + { + if (SidedefTextureMatch(side2, texturelongname)) + { + SidedefFillJob nj = new SidedefFillJob(); + nj.forward = forward; + nj.sidedef = side2; + stack.Push(nj); + } + } + } + } + + #endregion + + #region ================== Texture Alignment + + // This performs texture alignment along all walls that match with the same texture + // NOTE: This method uses the sidedefs marking to indicate which sides have been aligned + // When resetsidemarks is set to true, all sidedefs will first be marked false (not aligned). + // Setting resetsidemarks to false is usefull to align only within a specific selection + // (set the marked property to true for the sidedefs outside the selection) + public static void AutoAlignTextures(Sidedef start, ImageData texture, bool alignx, bool aligny, bool resetsidemarks) + { + Stack todo = new Stack(50); + float scalex = (General.Map.Config.ScaledTextureOffsets && !texture.WorldPanning) ? texture.Scale.x : 1.0f; + float scaley = (General.Map.Config.ScaledTextureOffsets && !texture.WorldPanning) ? texture.Scale.y : 1.0f; + + // Mark all sidedefs false (they will be marked true when the texture is aligned) + if (resetsidemarks) General.Map.Map.ClearMarkedSidedefs(false); + + // Begin with first sidedef + SidedefAlignJob first = new SidedefAlignJob(); + first.sidedef = start; + first.offsetx = start.OffsetX; + + first.forward = true; + todo.Push(first); + + // Continue until nothing more to align + while (todo.Count > 0) + { + // Get the align job to do + SidedefAlignJob j = todo.Pop(); + + if (j.forward) + { + Vertex v; + int forwardoffset; + int backwardoffset; + + // Apply alignment + if (alignx) j.sidedef.OffsetX = j.offsetx; + if (aligny) j.sidedef.OffsetY = (int)Math.Round((start.Sector.CeilHeight - j.sidedef.Sector.CeilHeight) / scaley) + start.OffsetY; + forwardoffset = j.offsetx + (int)Math.Round(j.sidedef.Line.Length / scalex); + backwardoffset = j.offsetx; + + j.sidedef.Marked = true; + + // Wrap the value within the width of the texture (to prevent ridiculous values) + // NOTE: We don't use ScaledWidth here because the texture offset is in pixels, not mappixels + if (texture.IsImageLoaded) + { + if (alignx) j.sidedef.OffsetX %= texture.Width; + if (aligny) j.sidedef.OffsetY %= texture.Height; + } + + // Add sidedefs forward (connected to the right vertex) + v = j.sidedef.IsFront ? j.sidedef.Line.End : j.sidedef.Line.Start; + AddSidedefsForAlignment(todo, v, true, forwardoffset, texture.LongName); + + // Add sidedefs backward (connected to the left vertex) + v = j.sidedef.IsFront ? j.sidedef.Line.Start : j.sidedef.Line.End; + AddSidedefsForAlignment(todo, v, false, backwardoffset, texture.LongName); + } + else + { + Vertex v; + int forwardoffset; + int backwardoffset; + + // Apply alignment + if (alignx) j.sidedef.OffsetX = j.offsetx - (int)Math.Round(j.sidedef.Line.Length / scalex); + if (aligny) j.sidedef.OffsetY = (int)Math.Round((start.Sector.CeilHeight - j.sidedef.Sector.CeilHeight) / scaley) + start.OffsetY; + forwardoffset = j.offsetx; + backwardoffset = j.offsetx - (int)Math.Round(j.sidedef.Line.Length / scalex); + + j.sidedef.Marked = true; + + // Wrap the value within the width of the texture (to prevent ridiculous values) + // NOTE: We don't use ScaledWidth here because the texture offset is in pixels, not mappixels + if (texture.IsImageLoaded) + { + if (alignx) j.sidedef.OffsetX %= texture.Width; + if (aligny) j.sidedef.OffsetY %= texture.Height; + } + + // Add sidedefs backward (connected to the left vertex) + v = j.sidedef.IsFront ? j.sidedef.Line.Start : j.sidedef.Line.End; + AddSidedefsForAlignment(todo, v, false, backwardoffset, texture.LongName); + + // Add sidedefs forward (connected to the right vertex) + v = j.sidedef.IsFront ? j.sidedef.Line.End : j.sidedef.Line.Start; + AddSidedefsForAlignment(todo, v, true, forwardoffset, texture.LongName); + } + } + } + + // This adds the matching, unmarked sidedefs from a vertex for texture alignment + private static void AddSidedefsForAlignment(Stack stack, Vertex v, bool forward, int offsetx, long texturelongname) + { + foreach (Linedef ld in v.Linedefs) + { + Sidedef side1 = forward ? ld.Front : ld.Back; + Sidedef side2 = forward ? ld.Back : ld.Front; + if ((ld.Start == v) && (side1 != null) && !side1.Marked) + { + if (SidedefTextureMatch(side1, texturelongname)) + { + SidedefAlignJob nj = new SidedefAlignJob(); + nj.forward = forward; + nj.offsetx = offsetx; + nj.sidedef = side1; + stack.Push(nj); + } + } + else if ((ld.End == v) && (side2 != null) && !side2.Marked) + { + if (SidedefTextureMatch(side2, texturelongname)) + { + SidedefAlignJob nj = new SidedefAlignJob(); + nj.forward = forward; + nj.offsetx = offsetx; + nj.sidedef = side2; + stack.Push(nj); + } + } + } + } + + // This checks if any of the sidedef texture match the given texture + private static bool SidedefTextureMatch(Sidedef sd, long texturelongname) + { + return ((sd.LongHighTexture == texturelongname) && sd.HighRequired()) || + ((sd.LongLowTexture == texturelongname) && sd.LowRequired()) || + ((sd.LongMiddleTexture == texturelongname) && (sd.MiddleRequired() || ((sd.MiddleTexture.Length > 0) && (sd.MiddleTexture[0] != '-')))); + } + + #endregion + + #region ================== Tags and Actions + + /// + /// This removes all tags on the marked geometry. + /// + public static void RemoveMarkedTags() + { + General.Map.Map.ForAllTags(RemoveTagHandler, true, null); + } + + // This removes tags + private static void RemoveTagHandler(MapElement element, bool actionargument, UniversalType type, ref int value, object obj) + { + value = 0; + } + + /// + /// This renumbers all tags on the marked geometry. + /// + public static void RenumberMarkedTags() + { + Dictionary tagsmap = new Dictionary(); + + // Collect the tag numbers used in the marked geometry + General.Map.Map.ForAllTags(CollectTagNumbersHandler, true, tagsmap); + + // Get new tags that are unique within unmarked geometry + List newtags = General.Map.Map.GetMultipleNewTags(tagsmap.Count, false); + + // Map the old tags with the new tags + int index = 0; + List oldkeys = new List(tagsmap.Keys); + foreach (int ot in oldkeys) tagsmap[ot] = newtags[index++]; + + // Now renumber the old tags with the new ones + General.Map.Map.ForAllTags(RenumberTagsHandler, true, tagsmap); + } + + // This collects tags in a dictionary + private static void CollectTagNumbersHandler(MapElement element, bool actionargument, UniversalType type, ref int value, Dictionary tagsmap) + { + if (value != 0) + tagsmap[value] = value; + } + + // This remaps tags from a dictionary + private static void RenumberTagsHandler(MapElement element, bool actionargument, UniversalType type, ref int value, Dictionary tagsmap) + { + if (value != 0) + value = tagsmap[value]; + } + + /// + /// This removes all actions on the marked geometry. + /// + public static void RemoveMarkedActions() + { + // Remove actions from things + foreach (Thing t in General.Map.Map.Things) + { + if (t.Marked) + { + t.Action = 0; + for (int i = 0; i < Thing.NUM_ARGS; i++) t.Args[i] = 0; + } + } + + // Remove actions from linedefs + foreach (Linedef l in General.Map.Map.Linedefs) + { + if (l.Marked) + { + l.Action = 0; + for (int i = 0; i < Linedef.NUM_ARGS; i++) l.Args[i] = 0; + } + } + } + + #endregion + + #region ================== Misc Exported Functions + + /// + /// This performs a Hermite spline interpolation and returns the result position. + /// Where u (0 - 1) is the wanted position on the curve between p1 (using tangent t1) and p2 (using tangent t2). + /// + public static Vector2D HermiteSpline(Vector2D p1, Vector2D t1, Vector2D p2, Vector2D t2, float u) + { + return D3DDevice.V2D(Vector2.Hermite(D3DDevice.V2(p1), D3DDevice.V2(t1), D3DDevice.V2(p2), D3DDevice.V2(t2), u)); + } + + /// + /// This performs a Hermite spline interpolation and returns the result position. + /// Where u (0 - 1) is the wanted position on the curve between p1 (using tangent t1) and p2 (using tangent t2). + /// + public static Vector3D HermiteSpline(Vector3D p1, Vector3D t1, Vector3D p2, Vector3D t2, float u) + { + return D3DDevice.V3D(Vector3.Hermite(D3DDevice.V3(p1), D3DDevice.V3(t1), D3DDevice.V3(p2), D3DDevice.V3(t2), u)); + } + + #endregion + } +} diff --git a/Source/Core/Geometry/Triangulation.cs b/Source/Core/Geometry/Triangulation.cs new file mode 100644 index 0000000..4baffec --- /dev/null +++ b/Source/Core/Geometry/Triangulation.cs @@ -0,0 +1,971 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using CodeImp.DoomBuilder.Geometry; +using CodeImp.DoomBuilder.Rendering; +using SlimDX.Direct3D9; +using System.Drawing; +using CodeImp.DoomBuilder.Map; +using System.Collections.ObjectModel; +using CodeImp.DoomBuilder.IO; + +#endregion + +namespace CodeImp.DoomBuilder.Geometry +{ + /// + /// Responsible for creating sector polygons. + /// Performs triangulation of sectors by using ear clipping. + /// + public sealed class Triangulation + { + #region ================== Delegates + +#if DEBUG + + // For debugging purpose only! + // These are not called in a release build + public delegate void ShowPolygon(LinkedList p); + public delegate void ShowEarClip(EarClipVertex[] found, LinkedList remaining); + public delegate void ShowRemaining(LinkedList remaining); + + // For debugging purpose only! + // These are not called in a release build + public ShowPolygon OnShowPolygon; + public ShowEarClip OnShowEarClip; + public ShowRemaining OnShowRemaining; + +#endif + + #endregion + + #region ================== Constants + + #endregion + + #region ================== Variables + + // Number of vertices per island + private ReadOnlyCollection islandvertices; + + // Vertices that result from the triangulation, 3 per triangle. + private ReadOnlyCollection vertices; + + // These sidedefs match with the vertices. If a vertex is not the start + // along a sidedef, this list contains a null entry for that vertex. + private ReadOnlyCollection sidedefs; + + // Temporary array for the sidedefs deserialization + private int[] sidedefindices; + + #endregion + + #region ================== Properties + + public ReadOnlyCollection IslandVertices { get { return islandvertices; } } + public ReadOnlyCollection Vertices { get { return vertices; } } + public ReadOnlyCollection Sidedefs { get { return sidedefs; } } + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + public static Triangulation Create(Sector sector) + { + Triangulation t = new Triangulation(); + t.Triangulate(sector); + return t; + } + + // Constructor + public Triangulation() + { + islandvertices = Array.AsReadOnly(new int[0]); + vertices = Array.AsReadOnly(new Vector2D[0]); + sidedefs = Array.AsReadOnly(new Sidedef[0]); + } + + // This performs the triangulation + public void Triangulate(Sector s) + { + // Initialize + List polys; + List islandslist = new List(); + List verticeslist = new List(); + List sidedefslist = new List(); + + // We have no destructor + GC.SuppressFinalize(this); + + /* + * This process is divided into several steps: + * + * 1) Tracing the sector lines to find clockwise outer polygons + * and counter-clockwise inner polygons. These are arranged in a + * polygon tree for the next step. + * + * 2) Cutting the inner polygons to make a flat list of only + * outer polygons. + * + * 3) Ear-clipping the polygons to create triangles. + * + */ + + // TRACING + polys = DoTrace(s); + + // CUTTING + DoCutting(polys); + + // EAR-CLIPPING + foreach (EarClipPolygon p in polys) + islandslist.Add(DoEarClip(p, verticeslist, sidedefslist)); + + // Make arrays + islandvertices = Array.AsReadOnly(islandslist.ToArray()); + vertices = Array.AsReadOnly(verticeslist.ToArray()); + sidedefs = Array.AsReadOnly(sidedefslist.ToArray()); + } + + #endregion + + #region ================== Serialization + + // Serialize / deserialize + internal void ReadWrite(IReadWriteStream s) + { + if (s.IsWriting) + { + s.wInt(islandvertices.Count); + for (int i = 0; i < islandvertices.Count; i++) s.wInt(islandvertices[i]); + + s.wInt(vertices.Count); + for (int i = 0; i < vertices.Count; i++) s.wVector2D(vertices[i]); + + s.wInt(sidedefs.Count); + for (int i = 0; i < sidedefs.Count; i++) + { + if (sidedefs[i] != null) + s.wInt(sidedefs[i].SerializedIndex); + else + s.wInt(-1); + } + } + else + { + int c; + + s.rInt(out c); + int[] islandverticeslist = new int[c]; + for (int i = 0; i < c; i++) s.rInt(out islandverticeslist[i]); + islandvertices = Array.AsReadOnly(islandverticeslist); + + s.rInt(out c); + Vector2D[] verticeslist = new Vector2D[c]; + for (int i = 0; i < c; i++) s.rVector2D(out verticeslist[i]); + vertices = Array.AsReadOnly(verticeslist); + + s.rInt(out c); + sidedefindices = new int[c]; + for (int i = 0; i < c; i++) s.rInt(out sidedefindices[i]); + } + } + + // After deserialization we need to find the actual sidedefs back + internal void PostDeserialize(MapSet map) + { + // Find our sidedefs + List sides = new List(sidedefindices.Length); + for (int i = 0; i < sidedefindices.Length; i++) + { + if (sidedefindices[i] >= 0) + sides.Add(map.SidedefIndices[sidedefindices[i]]); + else + sides.Add(null); + } + + // We don't need this array any longer + sidedefindices = null; + + // Keep readonly array + sidedefs = Array.AsReadOnly(sides.ToArray()); + } + + + #endregion + + #region ================== Tracing + + // This traces sector lines to create a polygon tree + private List DoTrace(Sector s) + { + Dictionary todosides = new Dictionary(s.Sidedefs.Count); + Dictionary ignores = new Dictionary(); + List root = new List(); + SidedefsTracePath path; + EarClipPolygon newpoly; + Vertex start; + + // Fill the dictionary + // The bool value is used to indicate lines which has been visited in the trace + foreach (Sidedef sd in s.Sidedefs) todosides.Add(sd, false); + + // First remove all sides that refer to the same sector on both sides of the line + RemoveDoubleSidedefReferences(todosides, s.Sidedefs); + + // Continue until all sidedefs have been processed + while (todosides.Count > 0) + { + // Reset all visited indicators + foreach (Sidedef sd in s.Sidedefs) if (todosides.ContainsKey(sd)) todosides[sd] = false; + + // Find the right-most vertex to start a trace with. + // This guarantees that we start out with an outer polygon and we just + // have to check if it is inside a previously found polygon. + start = FindRightMostVertex(todosides, ignores); + + // No more possible start vertex found? + // Then leave with what we have up till now. + if (start == null) break; + + // Trace to find a polygon + path = DoTracePath(new SidedefsTracePath(), start, null, s, todosides); + + // If tracing is not possible (sector not closed?) + // then add the start to the ignore list and try again later + if (path == null) + { + // Ignore vertex as start + ignores.Add(start, start); + } + else + { + // Remove the sides found in the path + foreach (Sidedef sd in path) todosides.Remove(sd); + + // Create the polygon + newpoly = path.MakePolygon(); + + // Determine where this polygon goes in our tree + foreach (EarClipPolygon p in root) + { + // Insert if it belongs as a child + if (p.InsertChild(newpoly)) + { + // Done + newpoly = null; + break; + } + } + + // Still not inserted in our tree? + if (newpoly != null) + { + // Then add it at root level as outer polygon + newpoly.Inner = false; + root.Add(newpoly); + } + } + } + + // Return result + return root; + } + + // This recursively traces a path + // Returns the resulting TracePath when the search is complete + // or returns null when no path found. + private SidedefsTracePath DoTracePath(SidedefsTracePath history, Vertex fromhere, Vertex findme, Sector sector, Dictionary sides) + { + SidedefsTracePath nextpath; + SidedefsTracePath result; + Vertex nextvertex; + List allsides; + + // Found the vertex we are tracing to? + if (fromhere == findme) return history; + + // On the first run, findme is null (otherwise the trace would end + // immeditely when it starts) so set findme here on the first run. + if (findme == null) findme = fromhere; + + // Make a list of sides referring to the same sector + allsides = new List(fromhere.Linedefs.Count * 2); + foreach (Linedef l in fromhere.Linedefs) + { + // Should we go along the front or back side? + // This is very important for clockwise polygon orientation! + if (l.Start == fromhere) + { + // Front side of line connected to sector? + if ((l.Front != null) && (l.Front.Sector == sector)) + { + // Visit here when not visited yet + if (sides.ContainsKey(l.Front) && !sides[l.Front]) allsides.Add(l.Front); + } + } + else + { + // Back side of line connected to sector? + if ((l.Back != null) && (l.Back.Sector == sector)) + { + // Visit here when not visited yet + if (sides.ContainsKey(l.Back) && !sides[l.Back]) allsides.Add(l.Back); + } + } + } + + // Previous line available? + if (history.Count > 0) + { + // This is done to ensure the tracing works along vertices that are shared by + // more than 2 lines/sides of the same sector. We must continue tracing along + // the first next smallest delta angle! This sorts the smallest delta angle to + // the top of the list. + SidedefAngleSorter sorter = new SidedefAngleSorter(history[history.Count - 1], fromhere); + allsides.Sort(sorter); + } + + // Go for all lines connected to this vertex + foreach (Sidedef s in allsides) + { + // Mark sidedef as visited and move to next vertex + sides[s] = true; + nextpath = new SidedefsTracePath(history, s); + if (s.Line.Start == fromhere) nextvertex = s.Line.End; else nextvertex = s.Line.Start; + + result = DoTracePath(nextpath, nextvertex, findme, sector, sides); + if (result != null) return result; + } + + // Nothing found + return null; + } + + // This removes all sidedefs which has a sidedefs on the other side + // of the same line that refers to the same sector. These are removed + // because they are useless and make the triangulation inefficient. + private void RemoveDoubleSidedefReferences(Dictionary todosides, ICollection sides) + { + // Go for all sides + foreach (Sidedef sd in sides) + { + // Double sided? + if (sd.Other != null) + { + // Referring to the same sector on both sides? + if (sd.Sector == sd.Other.Sector) + { + // Remove this one + todosides.Remove(sd); + } + } + } + } + + // This finds the right-most vertex to start tracing with + private Vertex FindRightMostVertex(Dictionary sides, Dictionary ignores) + { + Vertex found = null; + + // Go for all sides to find the right-most side + foreach (KeyValuePair sd in sides) + { + // First found? + if ((found == null) && !ignores.ContainsKey(sd.Key.Line.Start)) found = sd.Key.Line.Start; + if ((found == null) && !ignores.ContainsKey(sd.Key.Line.End)) found = sd.Key.Line.End; + + // Compare? + if (found != null) + { + // Check if more to the right than the previous found + if ((sd.Key.Line.Start.Position.x > found.Position.x) && !ignores.ContainsKey(sd.Key.Line.Start)) found = sd.Key.Line.Start; + if ((sd.Key.Line.End.Position.x > found.Position.x) && !ignores.ContainsKey(sd.Key.Line.End)) found = sd.Key.Line.End; + } + } + + // Return result + return found; + } + + #endregion + + #region ================== Cutting + + // This cuts into outer polygons to solve inner polygons and make the polygon tree flat + private void DoCutting(List polys) + { + Queue todo = new Queue(polys); + + // Begin processing outer polygons + while (todo.Count > 0) + { + // Get outer polygon to process + EarClipPolygon p = todo.Dequeue(); + + // Any inner polygons to work with? + if (p.Children.Count > 0) + { + // Go for all the children + foreach (EarClipPolygon c in p.Children) + { + // The children of the children are outer polygons again, + // so move them to the root and add for processing + polys.AddRange(c.Children); + foreach (EarClipPolygon sc in c.Children) todo.Enqueue(sc); + + // Remove from inner polygon + c.Children.Clear(); + } + + // Now do some cutting on this polygon to merge the inner polygons + MergeInnerPolys(p); + } + } + } + + // This takes an outer polygon and a set of inner polygons to start cutting on + private void MergeInnerPolys(EarClipPolygon p) + { + LinkedList todo = new LinkedList(p.Children); + LinkedListNode start; + LinkedListNode ip; + LinkedListNode found; + LinkedListNode foundstart; + + // Continue until no more inner polygons to process + while (todo.Count > 0) + { + // Find the inner polygon with the highest x vertex + found = null; + foundstart = null; + ip = todo.First; + while (ip != null) + { + start = FindRightMostVertex(ip.Value); + if ((foundstart == null) || (start.Value.Position.x > foundstart.Value.Position.x)) + { + // Found a better start + found = ip; + foundstart = start; + } + + // Next! + ip = ip.Next; + } + + // Remove from todo list + todo.Remove(found); + + // Get cut start and end + SplitOuterWithInner(foundstart, p, found.Value); + } + + // Remove the children, they should be merged in the polygon by now + p.Children.Clear(); + } + + // This finds the right-most vertex in an inner polygon to use for cut startpoint. + private LinkedListNode FindRightMostVertex(EarClipPolygon p) + { + LinkedListNode found = p.First; + LinkedListNode v = found.Next; + + // Go for all vertices to find the on with the biggest x value + while (v != null) + { + if (v.Value.Position.x > found.Value.Position.x) found = v; + v = v.Next; + } + + // Return result + return found; + } + + // This finds the cut coordinates and splits the other poly with inner vertices + private void SplitOuterWithInner(LinkedListNode start, EarClipPolygon p, EarClipPolygon inner) + { + LinkedListNode v1, v2; + LinkedListNode insertbefore = null; + float u, ul, bonus, foundu = float.MaxValue; + EarClipVertex split; + + // Create a line from start that goes beyond the right most vertex of p + LinkedListNode pr = FindRightMostVertex(p); + float startx = start.Value.Position.x; + float endx = pr.Value.Position.x + 10.0f; + Line2D starttoright = new Line2D(start.Value.Position, new Vector2D(endx, start.Value.Position.y)); + + // Calculate a small bonus (0.1 mappixel) + bonus = starttoright.GetNearestOnLine(new Vector2D(start.Value.Position.x + 0.1f, start.Value.Position.y)); + + // Go for all lines in the outer polygon + v1 = p.Last; + v2 = p.First; + while (v2 != null) + { + // Check if the line goes between startx and endx + if (((v1.Value.Position.x > startx) || + (v2.Value.Position.x > startx)) && + ((v1.Value.Position.x < endx) || + (v2.Value.Position.x < endx))) + { + // Find intersection + Line2D pl = new Line2D(v1.Value.Position, v2.Value.Position); + pl.GetIntersection(starttoright, out u, out ul); + if (float.IsNaN(u)) + { + // We have found a line that is perfectly horizontal + // (parallel to the cut scan line) Check if the line + // is overlapping the cut scan line. + if (v1.Value.Position.y == start.Value.Position.y) + { + // This is an exceptional situation which causes a bit of a problem, because + // this could be a previously made cut, which overlaps another line from the + // same cut and we have to determine which of the two we will join with. If we + // pick the wrong one, the polygon is no longer valid and triangulation will fail. + + // Calculate distance of each vertex in units + u = starttoright.GetNearestOnLine(v1.Value.Position); + ul = starttoright.GetNearestOnLine(v2.Value.Position); + + // Rule out vertices before the scan line + if (u < 0.0f) u = float.MaxValue; + if (ul < 0.0f) ul = float.MaxValue; + + float insert_u = Math.Min(u, ul); + + // Check in which direction the line goes. + if (v1.Value.Position.x > v2.Value.Position.x) + { + // The line goes from right to left (towards our start point) + // so we must always insert our cut after this line. + + // If the next line goes up, we consider this a better candidate than + // a horizontal line that goes from left to right (the other cut line) + // so we give it a small bonus. + LinkedListNode v3 = v2.Next ?? v2.List.First; + if (v3.Value.Position.y < v2.Value.Position.y) + insert_u -= bonus; + + // Remember this when it is a closer match + if (insert_u <= foundu) + { + insertbefore = v2.Next ?? v2.List.First; + foundu = insert_u; + } + } + else + { + // The line goes from left to right (away from our start point) + // so we must always insert our cut before this line. + + // If the previous line goes down, we consider this a better candidate than + // a horizontal line that goes from right to left (the other cut line) + // so we give it a small bonus. + LinkedListNode v3 = v1.Previous ?? v1.List.Last; + if (v3.Value.Position.y > v1.Value.Position.y) + insert_u -= bonus; + + // Remember this when it is a closer match + if (insert_u <= foundu) + { + insertbefore = v2; + foundu = insert_u; + } + } + } + } + // Found a closer match? + else if ((ul >= 0.0f) && (ul <= 1.0f) && (u > 0.0f) && (u <= foundu)) + { + // Found a closer intersection + insertbefore = v2; + foundu = u; + } + } + + // Next + v1 = v2; + v2 = v2.Next; + } + + // Found anything? + if (insertbefore != null) + { + Sidedef sd = (insertbefore.Previous == null) ? insertbefore.List.Last.Value.Sidedef : insertbefore.Previous.Value.Sidedef; + + // Find the position where we have to split the outer polygon + split = new EarClipVertex(starttoright.GetCoordinatesAt(foundu), null); + + // Insert manual split vertices + p.AddBefore(insertbefore, new EarClipVertex(split, sd)); + + // Start inserting from the start (do I make sense this time?) + v1 = start; + do + { + // Insert inner polygon vertex + p.AddBefore(insertbefore, new EarClipVertex(v1.Value)); + if (v1.Next != null) v1 = v1.Next; else v1 = v1.List.First; + } + while (v1 != start); + + // Insert manual split vertices + p.AddBefore(insertbefore, new EarClipVertex(start.Value, sd)); + if (split.Position != insertbefore.Value.Position) + p.AddBefore(insertbefore, new EarClipVertex(split, sd)); + } + } + + #endregion + + #region ================== Ear Clipping + + // This clips a polygon and returns the triangles + // The polygon may not have any holes or islands + // See: http://www.geometrictools.com/Documentation/TriangulationByEarClipping.pdf + private int DoEarClip(EarClipPolygon poly, List verticeslist, List sidedefslist) + { + LinkedList verts = new LinkedList(); + List convexes = new List(poly.Count); + LinkedList reflexes = new LinkedList(); + LinkedList eartips = new LinkedList(); + LinkedListNode n1, n2; + EarClipVertex v, v1, v2; + EarClipVertex[] t, t1, t2; + int countvertices = 0; + + // Go for all vertices to fill list + foreach (EarClipVertex vec in poly) + vec.SetVertsLink(verts.AddLast(vec)); + + // Remove any zero-length lines, these will give problems + n1 = verts.First; + do + { + // Continue until adjacent zero-length lines are removed + n2 = n1.Next ?? verts.First; + Vector2D d = n1.Value.Position - n2.Value.Position; + while ((Math.Abs(d.x) < 0.00001f) && (Math.Abs(d.y) < 0.00001f)) + { + n2.Value.Remove(); + n2 = n1.Next ?? verts.First; + if (n2 != null) d = n1.Value.Position - n2.Value.Position; else break; + } + + // Next! + n1 = n2; + } + while (n1 != verts.First); + + // Optimization: Vertices which have lines with the + // same angle are useless. Remove them! + n1 = verts.First; + while (n1 != null) + { + // Get the next vertex + n2 = n1.Next; + + // Get triangle for v + t = GetTriangle(n1.Value); + + // Check if both lines have the same angle + Line2D a = new Line2D(t[0].Position, t[1].Position); + Line2D b = new Line2D(t[1].Position, t[2].Position); + if (Math.Abs(Angle2D.Difference(a.GetAngle(), b.GetAngle())) < 0.00001f) + { + // Same angles, remove vertex + n1.Value.Remove(); + } + + // Next! + n1 = n2; + } + + // Go for all vertices to determine reflex or convex + foreach (EarClipVertex vv in verts) + { + // Add to reflex or convex list + if (IsReflex(GetTriangle(vv))) vv.AddReflex(reflexes); else convexes.Add(vv); + } + + // Go for all convex vertices to see if they are ear tips + foreach (EarClipVertex cv in convexes) + { + // Add when this is a valid ear + t = GetTriangle(cv); + if (CheckValidEar(t, reflexes)) cv.AddEarTip(eartips); + } + +#if DEBUG + if(OnShowPolygon != null) OnShowPolygon(verts); +#endif + + // Process ears until done + while ((eartips.Count > 0) && (verts.Count > 2)) + { + // Get next ear + v = eartips.First.Value; + t = GetTriangle(v); + + // Only save this triangle when it has an area + if (TriangleHasArea(t)) + { + // Add ear as triangle + AddTriangleToList(t, verticeslist, sidedefslist, (verts.Count == 3)); + countvertices += 3; + } + + // Remove this ear from all lists + v.Remove(); + v1 = t[0]; + v2 = t[2]; + +#if DEBUG + if(TriangleHasArea(t)) + { + if(OnShowEarClip != null) OnShowEarClip(t, verts); + } +#endif + + // Test first neighbour + t1 = GetTriangle(v1); + bool t1a = true; //TriangleHasArea(t1); + if (t1a && IsReflex(t1)) + { + // List as reflex if not listed yet + if (!v1.IsReflex) v1.AddReflex(reflexes); + v1.RemoveEarTip(); + } + else + { + // Remove from reflexes + v1.RemoveReflex(); + } + + // Test second neighbour + t2 = GetTriangle(v2); + bool t2a = true; //TriangleHasArea(t2); + if (t2a && IsReflex(t2)) + { + // List as reflex if not listed yet + if (!v2.IsReflex) v2.AddReflex(reflexes); + v2.RemoveEarTip(); + } + else + { + // Remove from reflexes + v2.RemoveReflex(); + } + + // Check if any neightbour have become a valid or invalid ear + if (!v1.IsReflex && (!t1a || CheckValidEar(t1, reflexes))) v1.AddEarTip(eartips); else v1.RemoveEarTip(); + if (!v2.IsReflex && (!t2a || CheckValidEar(t2, reflexes))) v2.AddEarTip(eartips); else v2.RemoveEarTip(); + } + +#if DEBUG + if(OnShowRemaining != null) OnShowRemaining(verts); +#endif + + // Dispose remaining vertices + foreach (EarClipVertex ecv in verts) ecv.Dispose(); + + // Return the number of vertices in the result + return countvertices; + } + + // This checks if a given ear is a valid (no intersections from reflex vertices) + private bool CheckValidEar(EarClipVertex[] t, LinkedList reflexes) + { + // Go for all reflex vertices + foreach (EarClipVertex rv in reflexes) + { + // Not one of the triangle corners? + if ((rv.Position != t[0].Position) && (rv.Position != t[1].Position) && (rv.Position != t[2].Position)) + { + // Return false on intersection + if (PointInsideTriangle(t, rv.MainListNode)) return false; + } + } + + // Valid ear! + return true; + } + + // This returns the 3-vertex array triangle for an ear + private EarClipVertex[] GetTriangle(EarClipVertex v) + { + EarClipVertex[] t = new EarClipVertex[3]; + t[0] = (v.MainListNode.Previous == null) ? v.MainListNode.List.Last.Value : v.MainListNode.Previous.Value; + t[1] = v; + t[2] = (v.MainListNode.Next == null) ? v.MainListNode.List.First.Value : v.MainListNode.Next.Value; + return t; + } + + // This checks if a vertex is reflex (corner > 180 deg) or convex (corner < 180 deg) + private bool IsReflex(EarClipVertex[] t) + { + // Return true when corner is > 180 deg + return (Line2D.GetSideOfLine(t[0].Position, t[2].Position, t[1].Position) < 0.0f); + } + + // This checks if a point is inside a triangle + // When the point is on an edge of the triangle, it depends on the lines + // adjacent to the point if it is considered inside or not + // NOTE: vertices in t must be in clockwise order! + private bool PointInsideTriangle(EarClipVertex[] t, LinkedListNode p) + { + // If the triangle has no area, there can never be a point inside + if (TriangleHasArea(t)) + { + float lineside01 = Line2D.GetSideOfLine(t[0].Position, t[1].Position, p.Value.Position); + float lineside12 = Line2D.GetSideOfLine(t[1].Position, t[2].Position, p.Value.Position); + float lineside20 = Line2D.GetSideOfLine(t[2].Position, t[0].Position, p.Value.Position); + float u_on_line = 0.5f; + + // If point p is on the line of an edge, find out where on the edge segment p is. + if (lineside01 == 0.0f) + u_on_line = Line2D.GetNearestOnLine(t[0].Position, t[1].Position, p.Value.Position); + else if (lineside12 == 0.0f) + u_on_line = Line2D.GetNearestOnLine(t[1].Position, t[2].Position, p.Value.Position); + else if (lineside20 == 0.0f) + u_on_line = Line2D.GetNearestOnLine(t[2].Position, t[0].Position, p.Value.Position); + + // If any of the lineside results are 0 then that means the point p lies on that edge and we + // need to test if the lines adjacent to the point p are in the triangle or not. + // If the lines are intersecting the triangle, we also consider the point inside. + if ((lineside01 == 0.0f) || (lineside12 == 0.0f) || (lineside20 == 0.0f)) + { + // When the point p is outside the edge segment, then it is not inside the triangle + if ((u_on_line < 0.0f) || (u_on_line > 1.0f)) + return false; + + // Point p is on an edge segment. We'll have to decide by it's lines if we call it inside or outside the triangle. + LinkedListNode p1 = p.Previous ?? p.List.Last; + LinkedListNode p2 = p.Next ?? p.List.First; + if (LineInsideTriangle(t, p.Value.Position, p1.Value.Position)) return true; + if (LineInsideTriangle(t, p.Value.Position, p2.Value.Position)) return true; + + return false; + } + else + { + return (lineside01 < 0.0f) && (lineside12 < 0.0f) && (lineside20 < 0.0f); + } + } + else + { + return false; + } + } + + // This checks if a line is inside a triangle (touching the triangle is allowed) + // NOTE: We already know p1 is on an edge segment of the triangle + private bool LineInsideTriangle(EarClipVertex[] t, Vector2D p1, Vector2D p2) + { + float s01 = Line2D.GetSideOfLine(t[0].Position, t[1].Position, p2); + float s12 = Line2D.GetSideOfLine(t[1].Position, t[2].Position, p2); + float s20 = Line2D.GetSideOfLine(t[2].Position, t[0].Position, p2); + float p2_on_edge = 2.0f; // somewhere outside the 0 .. 1 range + float p1_on_same_edge = 2.0f; + + // Test if p2 is inside the triangle + if ((s01 < 0.0f) && (s12 < 0.0f) && (s20 < 0.0f)) + { + // Line is inside triangle, because p2 is + return true; + } + // Test if p2 is on an edge of the triangle and if it is we would + // like to know where on the edge segment p2 is + else if (s01 == 0.0f) + { + p2_on_edge = Line2D.GetNearestOnLine(t[0].Position, t[1].Position, p2); + p1_on_same_edge = Line2D.GetSideOfLine(t[0].Position, t[1].Position, p1); + } + else if (s12 == 0.0f) + { + p2_on_edge = Line2D.GetNearestOnLine(t[1].Position, t[2].Position, p2); + p1_on_same_edge = Line2D.GetSideOfLine(t[1].Position, t[2].Position, p1); + } + else if (s20 == 0.0f) + { + p2_on_edge = Line2D.GetNearestOnLine(t[2].Position, t[0].Position, p2); + p1_on_same_edge = Line2D.GetSideOfLine(t[2].Position, t[0].Position, p1); + } + + // Is p2 actually on the edge segment? + if ((p2_on_edge >= 0.0f) && (p2_on_edge <= 1.0f)) + { + // If p1 is on the same edge (or the unlimited line of that edge) + // then the line is not inside this triangle. + if (p1_on_same_edge == 0.0f) + return false; + } + + // Do a complete line-triangle intersection test + // We already know p1 is not inside the triangle (possibly on an edge) + Line2D p = new Line2D(p1, p2); + Line2D t01 = new Line2D(t[0].Position, t[1].Position); + Line2D t12 = new Line2D(t[1].Position, t[2].Position); + Line2D t20 = new Line2D(t[2].Position, t[0].Position); + float pu, pt; + + // Test intersections + t01.GetIntersection(p, out pu, out pt); + if (!float.IsNaN(pu) && (pu >= 0.0f) && (pu <= 1.0f) && (pt >= 0.0f) && (pt <= 1.0f)) return true; + t12.GetIntersection(p, out pu, out pt); + if (!float.IsNaN(pu) && (pu >= 0.0f) && (pu <= 1.0f) && (pt >= 0.0f) && (pt <= 1.0f)) return true; + t20.GetIntersection(p, out pu, out pt); + if (!float.IsNaN(pu) && (pu >= 0.0f) && (pu <= 1.0f) && (pt >= 0.0f) && (pt <= 1.0f)) return true; + + return false; + } + + // This checks if the triangle has an area greater than 0 + private bool TriangleHasArea(EarClipVertex[] t) + { + return ((t[0].Position.x * (t[1].Position.y - t[2].Position.y) + + t[1].Position.x * (t[2].Position.y - t[0].Position.y) + + t[2].Position.x * (t[0].Position.y - t[1].Position.y)) != 0.0f); + } + + // This adds an array of vertices + private void AddTriangleToList(EarClipVertex[] triangle, List verticeslist, List sidedefslist, bool last) + { + // Create triangle + verticeslist.Add(triangle[0].Position); + sidedefslist.Add(triangle[0].Sidedef); + verticeslist.Add(triangle[1].Position); + sidedefslist.Add(triangle[1].Sidedef); + verticeslist.Add(triangle[2].Position); + if (!last) sidedefslist.Add(null); else sidedefslist.Add(triangle[2].Sidedef); + + // Modify the first earclipvertex of this triangle, it no longer lies along a sidedef + triangle[0].Sidedef = null; + } + + #endregion + } +} diff --git a/Source/Core/Geometry/Vector2D.cs b/Source/Core/Geometry/Vector2D.cs new file mode 100644 index 0000000..71bcbb4 --- /dev/null +++ b/Source/Core/Geometry/Vector2D.cs @@ -0,0 +1,356 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; + +#endregion + +namespace CodeImp.DoomBuilder.Geometry +{ + public struct Vector2D + { + #region ================== Constants + + private const float TINY_VALUE = 0.0000000001f; + + #endregion + + #region ================== Variables + + // Coordinates + public float x; + public float y; + + #endregion + + #region ================== Constructors + + // Constructor + public Vector2D(float x, float y) + { + this.x = x; + this.y = y; + } + + // Constructor + public Vector2D(Vector3D v) + { + this.x = v.x; + this.y = v.y; + } + + #endregion + + #region ================== Statics + + // Conversion to Vector3D + public static implicit operator Vector3D(Vector2D a) + { + return new Vector3D(a); + } + + // This adds two vectors + public static Vector2D operator +(Vector2D a, Vector2D b) + { + return new Vector2D(a.x + b.x, a.y + b.y); + } + + // This adds to a vector + public static Vector2D operator +(float a, Vector2D b) + { + return new Vector2D(a + b.x, a + b.y); + } + + // This adds to a vector + public static Vector2D operator +(Vector2D a, float b) + { + return new Vector2D(a.x + b, a.y + b); + } + + // This subtracts two vectors + public static Vector2D operator -(Vector2D a, Vector2D b) + { + return new Vector2D(a.x - b.x, a.y - b.y); + } + + // This subtracts from a vector + public static Vector2D operator -(Vector2D a, float b) + { + return new Vector2D(a.x - b, a.y - b); + } + + // This subtracts from a vector + public static Vector2D operator -(float a, Vector2D b) + { + return new Vector2D(a - b.x, a - b.y); + } + + // This reverses a vector + public static Vector2D operator -(Vector2D a) + { + return new Vector2D(-a.x, -a.y); + } + + // This scales a vector + public static Vector2D operator *(float s, Vector2D a) + { + return new Vector2D(a.x * s, a.y * s); + } + + // This scales a vector + public static Vector2D operator *(Vector2D a, float s) + { + return new Vector2D(a.x * s, a.y * s); + } + + // This scales a vector + public static Vector2D operator *(Vector2D a, Vector2D b) + { + return new Vector2D(a.x * b.x, a.y * b.y); + } + + // This scales a vector + public static Vector2D operator /(float s, Vector2D a) + { + return new Vector2D(a.x / s, a.y / s); + } + + // This scales a vector + public static Vector2D operator /(Vector2D a, float s) + { + return new Vector2D(a.x / s, a.y / s); + } + + // This scales a vector + public static Vector2D operator /(Vector2D a, Vector2D b) + { + return new Vector2D(a.x / b.x, a.y / b.y); + } + + // This calculates the dot product + public static float DotProduct(Vector2D a, Vector2D b) + { + // Calculate and return the dot product + return a.x * b.x + a.y * b.y; + } + + // This calculates the cross product + public static Vector2D CrossProduct(Vector2D a, Vector2D b) + { + Vector2D result = new Vector2D(); + + // Calculate and return the dot product + result.x = a.y * b.x; + result.y = a.x * b.y; + return result; + } + + // This compares a vector + public static bool operator ==(Vector2D a, Vector2D b) + { + return (a.x == b.x) && (a.y == b.y); + } + + // This compares a vector + public static bool operator !=(Vector2D a, Vector2D b) + { + return (a.x != b.x) || (a.y != b.y); + } + + // This reflects the vector v over mirror m + // Note that mirror m must be normalized! + // R = V - 2 * M * (M dot V) + public static Vector2D Reflect(Vector2D v, Vector2D m) + { + // Get the dot product of v and m + float dp = Vector2D.DotProduct(m, v); + + // Make the reflected vector + Vector2D mv = new Vector2D(); + mv.x = v.x - (2f * m.x * dp); + mv.y = v.y - (2f * m.y * dp); + + // Return the reflected vector + return mv; + } + + // This returns the reversed vector + public static Vector2D Reversed(Vector2D v) + { + // Return reversed vector + return new Vector2D(-v.x, -v.y); + } + + // This returns a vector from an angle + public static Vector2D FromAngle(float angle) + { + // Return vector from angle + return new Vector2D((float)Math.Sin(angle), -(float)Math.Cos(angle)); + } + + // This returns a vector from an angle with a given legnth + public static Vector2D FromAngle(float angle, float length) + { + // Return vector from angle + return FromAngle(angle) * length; + } + + // This calculates the angle + public static float GetAngle(Vector2D a, Vector2D b) + { + // Calculate and return the angle + return -(float)Math.Atan2(-(a.y - b.y), (a.x - b.x)) + (float)Math.PI * 0.5f; + } + + // This returns the square distance between two points + public static float DistanceSq(Vector2D a, Vector2D b) + { + Vector2D d = a - b; + return d.GetLengthSq(); + } + + // This returns the distance between two points + public static float Distance(Vector2D a, Vector2D b) + { + Vector2D d = a - b; + return d.GetLength(); + } + + // This returns the manhattan distance between two points + public static float ManhattanDistance(Vector2D a, Vector2D b) + { + Vector2D d = a - b; + return Math.Abs(d.x) + Math.Abs(d.y); + } + + #endregion + + #region ================== Methods + + // This returns the perpendicular vector by simply making a normal + public Vector2D GetPerpendicular() + { + return new Vector2D(-y, x); + } + + // This returns a vector with the sign of all components + public Vector2D GetSign() + { + return new Vector2D(Math.Sign(x), Math.Sign(y)); + } + + // This calculates the angle + public float GetAngle() + { + // Calculate and return the angle + return -(float)Math.Atan2(-y, x) + (float)Math.PI * 0.5f; + } + + // This calculates the length + public float GetLength() + { + // Calculate and return the length + return (float)Math.Sqrt(x * x + y * y); + } + + // This calculates the square length + public float GetLengthSq() + { + // Calculate and return the square length + return x * x + y * y; + } + + // This calculates the length + public float GetManhattanLength() + { + // Calculate and return the length + return Math.Abs(x) + Math.Abs(y); + } + + // This returns a normalized vector + public Vector2D GetNormal() + { + float lensq = this.GetLengthSq(); + if (lensq > TINY_VALUE) + { + // Divide each element by the length + float mul = 1f / (float)Math.Sqrt(lensq); + return new Vector2D(x * mul, y * mul); + } + else + { + // Cannot make normal + return new Vector2D(0f, 0f); + } + } + + // This scales the vector + public Vector2D GetScaled(float s) + { + // Scale the vector + return new Vector2D(x * s, y * s); + } + + // This changes the vector length + public Vector2D GetFixedLength(float l) + { + // Normalize, then scale + return this.GetNormal().GetScaled(l); + } + + // Output + public override string ToString() + { + return x + ", " + y; + } + + // Transform + public unsafe Vector2D GetTransformed(float offsetx, float offsety, float scalex, float scaley) + { + return new Vector2D((x + offsetx) * scalex, (y + offsety) * scaley); + } + + // Inverse Transform + public unsafe Vector2D GetInvTransformed(float invoffsetx, float invoffsety, float invscalex, float invscaley) + { + return new Vector2D((x * invscalex) + invoffsetx, (y * invscaley) + invoffsety); + } + + // Rotate (Added by Anders �strand 2008-05-18) + public unsafe Vector2D GetRotated(float theta) + { + double cos = Math.Cos(theta); + double sin = Math.Sin(theta); + double rx = cos * x - sin * y; + double ry = sin * x + cos * y; + return new Vector2D((float)rx, (float)ry); + } + + // Checks if the Vector has valid values for x and y + public bool IsFinite() + { + return !float.IsNaN(x) && !float.IsNaN(y) && !float.IsInfinity(x) && !float.IsInfinity(y); + } + + #endregion + } +} diff --git a/Source/Core/Geometry/Vector3D.cs b/Source/Core/Geometry/Vector3D.cs new file mode 100644 index 0000000..2bf2d06 --- /dev/null +++ b/Source/Core/Geometry/Vector3D.cs @@ -0,0 +1,296 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; + +#endregion + +namespace CodeImp.DoomBuilder.Geometry +{ + public struct Vector3D + { + #region ================== Constants + + private const float TINY_VALUE = 0.0000000001f; + + #endregion + + #region ================== Variables + + // Coordinates + public float x; + public float y; + public float z; + + #endregion + + #region ================== Constructors + + // Constructor + public Vector3D(float x, float y, float z) + { + this.x = x; + this.y = y; + this.z = z; + } + + // Constructor + public Vector3D(Vector2D v) + { + this.x = v.x; + this.y = v.y; + this.z = 0f; + } + + #endregion + + #region ================== Statics + + // Conversion to Vector2D + public static implicit operator Vector2D(Vector3D a) + { + return new Vector2D(a); + } + + // This adds two vectors + public static Vector3D operator +(Vector3D a, Vector3D b) + { + return new Vector3D(a.x + b.x, a.y + b.y, a.z + b.z); + } + + // This subtracts two vectors + public static Vector3D operator -(Vector3D a, Vector3D b) + { + return new Vector3D(a.x - b.x, a.y - b.y, a.z - b.z); + } + + // This reverses a vector + public static Vector3D operator -(Vector3D a) + { + return new Vector3D(-a.x, -a.y, -a.z); + } + + // This scales a vector + public static Vector3D operator *(float s, Vector3D a) + { + return new Vector3D(a.x * s, a.y * s, a.z * s); + } + + // This scales a vector + public static Vector3D operator *(Vector3D a, float s) + { + return new Vector3D(a.x * s, a.y * s, a.z * s); + } + + // This scales a vector + public static Vector3D operator *(Vector3D a, Vector3D b) + { + return new Vector3D(a.x * b.x, a.y * b.y, a.z * b.z); + } + + // This scales a vector + public static Vector3D operator /(float s, Vector3D a) + { + return new Vector3D(a.x / s, a.y / s, a.z / s); + } + + // This scales a vector + public static Vector3D operator /(Vector3D a, float s) + { + return new Vector3D(a.x / s, a.y / s, a.z / s); + } + + // This compares a vector + public static bool operator ==(Vector3D a, Vector3D b) + { + return (a.x == b.x) && (a.y == b.y) && (a.z == b.z); + } + + // This compares a vector + public static bool operator !=(Vector3D a, Vector3D b) + { + return (a.x != b.x) || (a.y != b.y) || (a.z != b.z); + } + + // This calculates the cross product + public static Vector3D CrossProduct(Vector3D a, Vector3D b) + { + Vector3D result = new Vector3D(); + + // Calculate and return the dot product + result.x = a.y * b.z - a.z * b.y; + result.y = a.z * b.x - a.x * b.z; + result.z = a.x * b.y - a.y * b.x; + return result; + } + + // This calculates the dot product + public static float DotProduct(Vector3D a, Vector3D b) + { + // Calculate and return the dot product + return a.x * b.x + a.y * b.y + a.z * b.z; + } + + // This reflects the vector v over mirror m + // Note that mirror m must be normalized! + public static Vector3D Reflect(Vector3D v, Vector3D m) + { + // Get the dot product of v and m + float dp = Vector3D.DotProduct(v, m); + + // Make the reflected vector + Vector3D mv = new Vector3D(); + mv.x = -v.x + 2f * m.x * dp; + mv.y = -v.y + 2f * m.y * dp; + mv.z = -v.z + 2f * m.z * dp; + + // Return the reflected vector + return mv; + } + + // This returns the reversed vector + public static Vector3D Reversed(Vector3D v) + { + // Return reversed vector + return new Vector3D(-v.x, -v.y, -v.z); + } + + // This returns a vector from an angle + public static Vector3D FromAngleXY(float angle) + { + // Return vector from angle + return new Vector3D((float)Math.Sin(angle), -(float)Math.Cos(angle), 0f); + } + + // This returns a vector from an angle with a given legnth + public static Vector3D FromAngleXY(float angle, float length) + { + // Return vector from angle + return FromAngleXY(angle) * length; + } + + // This returns a vector from an angle with a given legnth + public static Vector3D FromAngleXYZ(float anglexy, float anglez) + { + // Calculate x y and z + float ax = (float)Math.Sin(anglexy) * (float)Math.Cos(anglez); + float ay = -(float)Math.Cos(anglexy) * (float)Math.Cos(anglez); + float az = (float)Math.Sin(anglez); + + // Return vector + return new Vector3D(ax, ay, az); + } + + #endregion + + #region ================== Methods + + // This calculates the angle + public float GetAngleXY() + { + // Calculate and return the angle + return -(float)Math.Atan2(-y, x) + (float)Math.PI * 0.5f; + } + + // This calculates the angle + public float GetAngleZ() + { + Vector2D xy = new Vector2D(x, y); + + // Calculate and return the angle + return (float)Math.Atan2(xy.GetLength(), z) + (float)Math.PI * 0.5f; + } + + // This calculates the length + public float GetLength() + { + // Calculate and return the length + return (float)Math.Sqrt(x * x + y * y + z * z); + } + + // This calculates the squared length + public float GetLengthSq() + { + // Calculate and return the length + return x * x + y * y + z * z; + } + + // This calculates the length + public float GetManhattanLength() + { + // Calculate and return the length + return Math.Abs(x) + Math.Abs(y) + Math.Abs(z); + } + + // This normalizes the vector + public Vector3D GetNormal() + { + float lensq = this.GetLengthSq(); + if (lensq > TINY_VALUE) + { + // Divide each element by the length + float mul = 1f / (float)Math.Sqrt(lensq); + return new Vector3D(x * mul, y * mul, z * mul); + } + else + { + // Cannot normalize + return new Vector3D(0f, 0f, 0f); + } + } + + // This scales the vector + public Vector3D GetScaled(float s) + { + // Scale the vector + return new Vector3D(x * s, y * s, z * s); + } + + // This changes the vector length + public Vector3D GetFixedLength(float l) + { + // Normalize, then scale + return this.GetNormal().GetScaled(l); + } + + // This checks if the vector is normalized + public bool IsNormalized() + { + return (Math.Abs(GetLengthSq() - 1.0f) < 0.0001f); + } + + // Output + public override string ToString() + { + return x + ", " + y + ", " + z; + } + + // Checks if the Vector has valid values for x, y and z + public bool IsFinite() + { + return !float.IsNaN(x) && !float.IsNaN(y) && !float.IsNaN(z) && !float.IsInfinity(x) && !float.IsInfinity(y) && !float.IsInfinity(z); + } + + + #endregion + } +} diff --git a/Source/Core/IO/ClippedStream.cs b/Source/Core/IO/ClippedStream.cs new file mode 100644 index 0000000..6a32d95 --- /dev/null +++ b/Source/Core/IO/ClippedStream.cs @@ -0,0 +1,275 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.IO; + +#endregion + +namespace CodeImp.DoomBuilder.IO +{ + internal class ClippedStream : Stream + { + #region ================== Variables + + // Base stream + private Stream basestream; + + // Data limit + private int offset; + private int length; + private long position; + + // Disposing + private bool isdisposed = false; + + #endregion + + #region ================== Properties + + public override long Length { get { return length; } } + public override long Position { get { return position; } set { this.Seek(value, SeekOrigin.Begin); } } + public override bool CanRead { get { return basestream.CanRead; } } + public override bool CanSeek { get { return basestream.CanSeek; } } + public override bool CanWrite { get { return basestream.CanWrite; } } + public bool IsDisposed { get { return isdisposed; } } + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + public ClippedStream(Stream basestream, int offset, int length) + { + // Can only create from a stream that can seek + if (!basestream.CanSeek) throw new ArgumentException("ClippedStream can only be created with a Stream that allows Seeking."); + + // Initialize + this.basestream = basestream; + this.position = 0; + this.offset = offset; + this.length = length; + + // We have no destructor + GC.SuppressFinalize(this); + } + + // Disposer + public new void Dispose() + { + // Not already disposed? + if (!isdisposed) + { + // Already set isdisposed to prevent recursion + isdisposed = true; + + // Clean up + basestream = null; + + // Dispose base + base.Dispose(); + } + } + + #endregion + + #region ================== Methods + + // This flushes the written changes + public override void Flush() + { + // Flush base stream + basestream.Flush(); + } + + // This reads from the stream + public override int Read(byte[] buffer, int offset, int count) + { + // Check if this exceeds limits + if ((this.position + count) > (this.length + 1)) + { + // Read only within limits + count = this.length - (int)this.position; + } + + // Anything to read? + if (count > 0) + { + // Seek if needed + if (basestream.Position != (this.offset + this.position)) + basestream.Seek(this.offset + this.position, SeekOrigin.Begin); + + // Read from base stream + position += count; + return basestream.Read(buffer, offset, count); + } + else + { + return 0; + } + } + + // This writes to the stream + public override void Write(byte[] buffer, int offset, int count) + { + // Check if this exceeds limits + if ((this.position + count) > (this.length + 1)) + throw new ArgumentException("Attempted to write outside the range of the stream."); + + // Seek if needed + if (basestream.Position != (this.offset + this.position)) + basestream.Seek(this.offset + this.position, SeekOrigin.Begin); + + // Read from base stream + position += count; + basestream.Write(buffer, offset, count); + } + + // Seek within clipped buffer + public override long Seek(long offset, SeekOrigin origin) + { + // Seeking from beginning + if (origin == SeekOrigin.Begin) + { + // Check if this exceeds limits + if ((offset > this.length) || (offset < 0)) + throw new ArgumentException("Attempted to seek outside the range of the stream."); + + // Seek + position = basestream.Seek(this.offset + offset, SeekOrigin.Begin) - this.offset; + } + // Seeking from current position + else if (origin == SeekOrigin.Current) + { + // Check if this exceeds limits + if ((this.position + offset > this.length) || (this.position + offset < 0)) + throw new ArgumentException("Attempted to seek outside the range of the stream."); + + // Seek + position = basestream.Seek(this.offset + this.position + offset, SeekOrigin.Begin) - this.offset; + } + // Seeking from end + else + { + // Check if this exceeds limits + if ((offset > 0) || (this.length + offset < 0)) + throw new ArgumentException("Attempted to seek outside the range of the stream."); + + // Seek + position = basestream.Seek(this.offset + this.length + offset, SeekOrigin.Begin) - this.offset; + } + + // Return new position + return position; + } + + // Change the length of the steam + public override void SetLength(long value) + { + // Not supported + throw new NotSupportedException("This operation is not supported."); + } + + // Asynchronous read from stream + public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state) + { + // Check if this exceeds limits + if ((this.position + count) > (this.length + 1)) + throw new ArgumentException("Attempted to read outside the range of the stream."); + + // Seek if needed + if (basestream.Position != (this.offset + this.position)) + basestream.Seek(this.offset + this.position, SeekOrigin.Begin); + + // Read + position += count; + return base.BeginRead(buffer, offset, count, callback, state); + } + + // Asynchronous write to stream + public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state) + { + // Check if this exceeds limits + if ((this.position + count) > (this.length + 1)) + throw new ArgumentException("Attempted to write outside the range of the stream."); + + // Seek if needed + if (basestream.Position != (this.offset + this.position)) + basestream.Seek(this.offset + this.position, SeekOrigin.Begin); + + // Write + position += count; + return base.BeginWrite(buffer, offset, count, callback, state); + } + + // This closes the stream + public override void Close() + { + basestream = null; + base.Close(); + this.Dispose(); + } + + // This reads a single byte from the stream + public override int ReadByte() + { + // Check if this exceeds limits + if ((this.position + 1) > (this.length + 1)) + throw new ArgumentException("Attempted to read outside the range of the stream."); + + // Seek if needed + if (basestream.Position != (this.offset + this.position)) + basestream.Seek(this.offset + this.position, SeekOrigin.Begin); + + // Read from base stream + position++; + return basestream.ReadByte(); + } + + // This writes a single byte to the stream + public override void WriteByte(byte value) + { + // Check if this exceeds limits + if ((this.position + 1) > (this.length + 1)) + throw new ArgumentException("Attempted to write outside the range of the stream."); + + // Seek if needed + if (basestream.Position != (this.offset + this.position)) + basestream.Seek(this.offset + this.position, SeekOrigin.Begin); + + // Read from base stream + position++; + basestream.WriteByte(value); + } + + // This returns all the bytes in the stream + public byte[] ReadAllBytes() + { + byte[] bytes = new byte[length]; + Seek(0, SeekOrigin.Begin); + Read(bytes, 0, length); + return bytes; + } + + #endregion + } +} diff --git a/Source/Core/IO/Configuration.cs b/Source/Core/IO/Configuration.cs new file mode 100644 index 0000000..5f71437 --- /dev/null +++ b/Source/Core/IO/Configuration.cs @@ -0,0 +1,1505 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== CFG file structure syntax + +/* +' ==================================================================================== +' CONFIGURATION FILE STRUCTURE SYNTAX +' ==================================================================================== +' +' Whitepace is always allowed. This includes spaces, tabs +' linefeeds (10) and carriage returns (13) +' +' Keys may not have spaces or assignment operator = in them. +' +' Comments start with // (unless used within strings) +' and count as comment for the rest of the line. Or use /* and */ /* +' to mark the beginning and end of a comment. +' +' Simple setting: +' +' key = value; +' +' Example: speed = 345; +' cars = 8; +' +' Strings must be in quotes. +' +' Example: nickname = "Gherkin"; +' altnick = "Gherk inn"; +' +' String Escape Sequences: +' \n New line (10) +' \r Carriage return (13) +' \t Tab (9) +' \" Double-quotation mark +' \\ Backslash +' \000 Any ASCII character (MUST be 3 digits! So for 13 you use \013) +' +' Decimals ALWAYS use a dot, NEVER comma! +' +' Example: pressure = 15.29; +' acceleration = 1.0023; +' +' true, false and null are valid keywords. +' null values can be left out. +' +' In this example, both items are null. +' Example: myitem = null; +' myotheritem; +' +' Structures must use brackets. +' +' Structure Example: +' +' key +' { +' key = value; +' key = value; +' +' key +' { +' key = value; +' key = value; +' key = value; +' } +' +' key = value; +' key = value; +' key = value; +' key = value; +' key = value; +' } +' +' As you can see, structures inside structures are allowed +' and you may go as deep as you want. Note that only the root structures +' can be readed from config using ReadSetting. ReadSetting will return a +' Dictionary object containing everything in that root structure. +' +' Key names must be unique within their scope. +' +' This is NOT allowed, it may not have 'father' more +' than once in the same scope: +' +' mother = 45; +' father = 52; +' +' father +' { +' length = 1.87; +' } +' +' This however is allowed, because father +' now exists in a different scope: +' +' mother = 45; +' father = 52; +' +' parents +' { +' father = 52; +' } +' +' This too is allowed, both 'age' are in a different scope: +' +' mother +' { +' age = 45; +' } +' +' father +' { +' age = 52; +' } +*/ + +#endregion + +#region ================== Namespaces + +using System; +using System.IO; +using System.Text; +using System.Globalization; +using System.Collections; +using System.Collections.Specialized; + +#endregion + +namespace CodeImp.DoomBuilder.IO +{ + public sealed class Configuration + { + #region ================== Constants + + // Path seperator + public const string DEFAULT_SEPERATOR = "."; + + // Error strings + private const string ERROR_KEYMISSING = "Missing key name in assignment or scope."; + private const string ERROR_KEYSPACES = "Spaces not allowed in key names."; + private const string ERROR_ASSIGNINVALID = "Invalid assignment. Missing a previous terminator symbol?"; + private const string ERROR_VALUEINVALID = "Invalid value in assignment. Missing a previous terminator symbol?"; + private const string ERROR_VALUETOOBIG = "Value too big."; + private const string ERROR_KEYNOTUNQIUE = "Key is not unique within scope."; + private const string ERROR_KEYWORDUNKNOWN = "Unknown keyword in assignment. Missing a previous terminator symbol?"; + private const string ERROR_UNEXPECTED_END = "Unexpected end of data. Missing a previous terminator symbol?"; + private const string ERROR_UNKNOWN_FUNCTION = "Unknown function call."; + private const string ERROR_INVALID_ARGS = "Invalid function arguments."; + private const string ERROR_INCLUDE_UNSUPPORTED = "Include function is not supported in data parsed from stream."; + + #endregion + + #region ================== Variables + + // Error result + private bool cpErrorResult = false; + private string cpErrorDescription = ""; + private int cpErrorLine = 0; + private string cpErrorFile = ""; + + // Configuration root + private IDictionary root = null; + + #endregion + + #region ================== Properties + + // Properties + public bool ErrorResult { get { return cpErrorResult; } } + public string ErrorDescription { get { return cpErrorDescription; } } + public int ErrorLine { get { return cpErrorLine; } } + public string ErrorFile { get { return cpErrorFile; } } + public IDictionary Root { get { return root; } set { root = value; } } + public bool Sorted { get { return (root is ListDictionary); } } + + #endregion + + #region ================== Constructor / Destructor + + // Constructor + public Configuration() + { + // Standard new configuration + NewConfiguration(); + + // We have no destructor + GC.SuppressFinalize(this); + } + + // Constructor + public Configuration(bool sorted) + { + // Standard new configuration + NewConfiguration(sorted); + + // We have no destructor + GC.SuppressFinalize(this); + } + + // Constructor to load a file immediately + public Configuration(string filename) + { + // Load configuration from file + LoadConfiguration(filename); + + // We have no destructor + GC.SuppressFinalize(this); + } + + // Constructor to load a file immediately + public Configuration(string filename, bool sorted) + { + // Load configuration from file + LoadConfiguration(filename, sorted); + + // We have no destructor + GC.SuppressFinalize(this); + } + + #endregion + + #region ================== Private Methods + + // This merges two structures + private static IDictionary Combine(IDictionary d1, IDictionary d2, bool sorted) + { + // Create new dictionary + IDictionary result; + if (sorted) result = new ListDictionary(); else result = new Hashtable(); + + // Copy all items from d1 to result + IDictionaryEnumerator d1e = d1.GetEnumerator(); + while (d1e.MoveNext()) result.Add(d1e.Key, d1e.Value); + + // Go for all items in d2 + IDictionaryEnumerator d2e = d2.GetEnumerator(); + while (d2e.MoveNext()) + { + // Check if this is another Hashtable + if (d2e.Value is IDictionary) + { + // Check if already in result + if (result.Contains(d2e.Key) && (result[d2e.Key] is IDictionary)) + { + // Modify result + result[d2e.Key] = Combine((IDictionary)result[d2e.Key], (IDictionary)d2e.Value, sorted); + } + else + { + // Copy from d2 + if (sorted) + { + // Sorted combine + result[d2e.Key] = Combine(new ListDictionary(), (IDictionary)d2e.Value, sorted); + } + else + { + // Unsorted combine + result[d2e.Key] = Combine(new Hashtable(), (IDictionary)d2e.Value, sorted); + } + } + } + else + { + // Check if also in d1 + if (result.Contains(d2e.Key)) + { + // Modify result + result[d2e.Key] = d2e.Value; + } + else + { + // Copy + result.Add(d2e.Key, d2e.Value); + } + } + } + + // Return result + return result; + } + + // This is called by all the ReadSetting overloads to perform the read + private bool CheckSetting(IDictionary dic, string setting, string pathseperator) + { + IDictionary cs = null; + + // Split the path in an array + string[] keys = setting.Split(pathseperator.ToCharArray()); + + // Get the root item + object item = dic; + + // Go for each item + for (int i = 0; i < keys.Length; i++) + { + // Check if the current item is of ConfigStruct type + if (item is IDictionary) + { + // Check if the key is valid + if (ValidateKey(null, keys[i].Trim(), "", -1) == true) + { + // Cast to ConfigStruct + cs = (IDictionary)item; + + // Check if the requested item exists + if (cs.Contains(keys[i]) == true) + { + // Set the item to the next item + item = cs[keys[i]]; + } + else + { + // Key not found + return false; + } + } + else + { + // Invalid key in path + return false; + } + } + else + { + // Unable to go any further + return false; + } + } + + // Return result + return true; + } + + // This is called by all the ReadSetting overloads to perform the read + private object ReadAnySetting(string setting, object defaultsetting, string pathseperator) { return ReadAnySetting(root, setting, defaultsetting, pathseperator); } + private object ReadAnySetting(IDictionary dic, string setting, object defaultsetting, string pathseperator) { return ReadAnySetting(dic, "", -1, setting, defaultsetting, pathseperator); } + private object ReadAnySetting(IDictionary dic, string file, int line, string setting, object defaultsetting, string pathseperator) + { + IDictionary cs = null; + + // Split the path in an array + string[] keys = setting.Split(pathseperator.ToCharArray()); + + // Get the root item + object item = dic; + + // Go for each item + foreach (string key in keys) + { + // Check if the current item is of ConfigStruct type + if (item is IDictionary) + { + // Check if the key is valid + if (ValidateKey(null, key.Trim(), file, line) == true) + { + // Cast to ConfigStruct + cs = (IDictionary)item; + + // Check if the requested item exists + if (cs.Contains(key) == true) + { + // Set the item to the next item + item = cs[key]; + } + else + { + // Key not found + // return default setting + return defaultsetting; + } + } + else + { + // Invalid key in path + // return default setting + return defaultsetting; + } + } + else + { + // Unable to go any further + // return default setting + return defaultsetting; + } + } + + // Return the item + return item; + } + + + // This returns a string added with escape characters + private string EscapedString(string str) + { + // Replace the \ with \\ first! + str = str.Replace("\\", "\\\\"); + str = str.Replace("\n", "\\n"); + str = str.Replace("\r", "\\r"); + str = str.Replace("\t", "\\t"); + str = str.Replace("\"", "\\\""); + + // Return result + return str; + } + + + // This raises an error + private void RaiseError(string file, int line, string description) + { + // Raise error + if (!cpErrorResult) + { + cpErrorResult = true; + cpErrorDescription = description; + cpErrorLine = line; + cpErrorFile = file; + } + } + + + // This validates a given key and sets + // error properties if key is invalid and errorline > -1 + private bool ValidateKey(IDictionary container, string key, string file, int errorline) + { + bool validateresult; + + // Check if key is an empty string + if (key == "") + { + // ERROR: Missing key name in statement + if (errorline > -1) RaiseError(file, errorline, ERROR_KEYMISSING); + validateresult = false; + } + else + { + // Check if there are spaces in the key + if (key.IndexOfAny(" ".ToCharArray()) > -1) + { + // ERROR: Spaces not allowed in key names + if (errorline > -1) RaiseError(file, errorline, ERROR_KEYSPACES); + validateresult = false; + } + else + { + // Check if we can test existance + if (container != null) + { + /* + // Test if the key exists in this container + if(container.Contains(key) == true) + { + // ERROR: Key is not unique within struct + if(errorline > -1) RaiseError(file, errorline, ERROR_KEYNOTUNQIUE); + validateresult = false; + } + else + */ + { + // Key OK + validateresult = true; + } + } + else + { + // Key OK + validateresult = true; + } + } + } + + // Return result + return validateresult; + } + + + // This validates a given keyword and sets + // error properties if keyword is invalid and errorline > -1 + private bool ValidateKeyword(string keyword, string file, int errorline) + { + bool validateresult; + + // Check if key is an empty string + if (keyword == "") + { + // ERROR: Missing key name in statement + if (errorline > -1) RaiseError(file, errorline, ERROR_ASSIGNINVALID); + validateresult = false; + } + else + { + // Check if there are spaces in the key + if (keyword.IndexOfAny(" ".ToCharArray()) > -1) + { + // ERROR: Spaces not allowed in key names + if (errorline > -1) RaiseError(file, errorline, ERROR_ASSIGNINVALID); + validateresult = false; + } + else + { + // Key OK + validateresult = true; + } + } + + // Return result + return validateresult; + } + + #endregion + + #region ================== Parsing + + // This parses an assignment + private object ParseAssignment(ref string file, ref string data, ref int pos, ref int line) + { + object val = null; + + while ((pos < data.Length) && !cpErrorResult) + { + // Get current character + char c = data[pos++]; + + // Check for string opening + if (c == '\"') + { + // Now parsing a string + val = ParseString(ref file, ref data, ref pos, ref line); + if (cpErrorResult) return null; + } + // Check for numeric character + else if ("0123456789-.&".IndexOf(c.ToString(CultureInfo.InvariantCulture)) > -1) + { + // Go one byte back, because this + // byte is part of the number! + pos--; + + // Now parsing a number + val = ParseNumber(ref file, ref data, ref pos, ref line); + if (cpErrorResult) return null; + } + // Check for new line + else if (c == '\n') + { + // Count the new line + line++; + } + // Check if assignment ends + else if (c == ';') + { + // End of assignment + return val; + } + // Otherwise (if not whitespace) it is a keyword + else if ((c != ' ') && (c != '\t')) + { + // Go one byte back, because this + // byte is part of the keyword! + pos--; + + // Now parsing a keyword + val = ParseKeyword(ref file, ref data, ref pos, ref line); + if (cpErrorResult) return null; + } + } + + RaiseError(file, line, ERROR_UNEXPECTED_END); + return null; + } + + + // This parses a string + private string ParseString(ref string file, ref string data, ref int pos, ref int line) + { + string val = ""; + + // In escape sequence? + bool escape = false; + + while ((pos < data.Length) && !cpErrorResult) + { + // Get current character + char c = data[pos++]; + + // Check if in an escape sequence + if (escape) + { + // What character? + switch (c) + { + case '\\': val += "\\"; break; + case 'n': val += "\n"; break; + case '\"': val += "\""; break; + case 'r': val += "\r"; break; + case 't': val += "\t"; break; + default: + + // Is it a number? + if ("0123456789".IndexOf(c.ToString(CultureInfo.InvariantCulture)) > -1) + { + int vv = 0; + char vc = '0'; + + // Convert the next 3 characters to a number + string v = data.Substring(pos, 3); + try { vv = System.Convert.ToInt32(v.Trim(), CultureInfo.InvariantCulture); } + catch (System.FormatException) + { + // ERROR: Invalid value in assignment + RaiseError(file, line, ERROR_VALUEINVALID); + return null; + } + + // Convert the number to a char + try { vc = System.Convert.ToChar(vv, CultureInfo.InvariantCulture); } + catch (System.FormatException) + { + // ERROR: Invalid value in assignment + RaiseError(file, line, ERROR_VALUEINVALID); + return null; + } + + // Add the char + val += vc.ToString(CultureInfo.InvariantCulture); + } + else + { + // Add the character as it is + val += c.ToString(CultureInfo.InvariantCulture); + } + + // Leave switch + break; + } + + // End of escape sequence + escape = false; + } + else + { + // Check for sequence start + if (c == '\\') + { + // Next character is of escape sequence + escape = true; + } + // Check if string ends + else if (c == '\"') + { + return val; + } + // Check for new line + else if (c == '\n') + { + // Count the new line + line++; + } + // Everything else is just part of string + else + { + // Add to value + val += c.ToString(CultureInfo.InvariantCulture); + } + } + } + + RaiseError(file, line, ERROR_UNEXPECTED_END); + return null; + } + + + // Parsing a number + private object ParseNumber(ref string file, ref string data, ref int pos, ref int line) + { + string val = ""; + + while ((pos < data.Length) && !cpErrorResult) + { + // Get current character + char c = data[pos++]; + + // Check if number ends here + if ((c == ';') || (c == ',') || (c == ')')) + { + // One byte back, because this + // is also the end of the assignment + pos--; + + // Floating point? + if (val.IndexOf("f") > -1) + { + float fval = 0; + + // Convert to float (remove the f first) + try { fval = System.Convert.ToSingle(val.Trim().Replace("f", ""), CultureInfo.InvariantCulture); } + catch (System.FormatException) + { + // ERROR: Invalid value in assignment + RaiseError(file, line, ERROR_VALUEINVALID); + return null; + } + return fval; + } + else + { + int ival = 0; + long lval = 0; + + // Convert to int + try + { + // Convert to value + ival = System.Convert.ToInt32(val.Trim(), CultureInfo.InvariantCulture); + return ival; + } + catch (System.OverflowException) + { + // Too large for Int32, try Int64 + try + { + // Convert to value + lval = System.Convert.ToInt64(val.Trim(), CultureInfo.InvariantCulture); + return lval; + } + catch (System.OverflowException) + { + // Too large for Int64, return error + RaiseError(file, line, ERROR_VALUETOOBIG); + return null; + } + catch (System.FormatException) + { + // ERROR: Invalid value in assignment + RaiseError(file, line, ERROR_VALUEINVALID); + return null; + } + } + catch (System.FormatException) + { + // ERROR: Invalid value in assignment + RaiseError(file, line, ERROR_VALUEINVALID); + return null; + } + } + } + // Check for new line + else if (c == '\n') + { + // Count the new line + line++; + } + // Everything else is part of the value + else + { + val += c.ToString(CultureInfo.InvariantCulture); + } + } + + RaiseError(file, line, ERROR_UNEXPECTED_END); + return null; + } + + + // Parsing a keyword + private object ParseKeyword(ref string file, ref string data, ref int pos, ref int line) + { + string val = ""; + + while ((pos < data.Length) && !cpErrorResult) + { + // Get current character + char c = data[pos++]; + + // Check if keyword ends + if ((c == ';') || (c == ',') || (c == ')')) + { + // One byte back, because this + // is also the end of the assignment + pos--; + + // Validate the keyword + if (ValidateKeyword(val.Trim(), file, line)) + { + // Return result depending on the keyword + switch (val.Trim().ToLowerInvariant()) + { + case "true": return (bool)true; + case "false": return (bool)false; + case "null": return null; + default: RaiseError(file, line, ERROR_KEYWORDUNKNOWN); return null; + } + } + } + // Check for new line + else if (c == '\n') + { + // Count the new line + line++; + } + // Everything else is just part of keyword + else + { + // Add to value + val += c.ToString(CultureInfo.InvariantCulture); + } + } + + RaiseError(file, line, ERROR_UNEXPECTED_END); + return null; + } + + + // This includes another file + private void FunctionInclude(IDictionary cs, ArrayList args, ref string file, int line) + { + string data; + + if (string.IsNullOrEmpty(file)) RaiseError(file, line, ERROR_INCLUDE_UNSUPPORTED); + if (args.Count < 1) RaiseError(file, line, ERROR_INVALID_ARGS); + if (!(args[0] is string)) RaiseError(file, line, ERROR_INVALID_ARGS + " Expected a string for argument 1."); + if ((args.Count > 1) && !(args[1] is string)) RaiseError(file, line, ERROR_INVALID_ARGS + " Expected a string for argument 2."); + if (cpErrorResult) return; + + // Determine the full path of the file to include + string includefile = Path.GetDirectoryName(file) + Path.DirectorySeparatorChar + args[0].ToString(); + + try + { + // Load the file contents + FileStream fstream = File.OpenRead(includefile); + byte[] fbuffer = new byte[fstream.Length]; + fstream.Read(fbuffer, 0, fbuffer.Length); + fstream.Close(); + + // Convert byte array to string + data = Encoding.UTF8.GetString(fbuffer); + } + catch (Exception e) + { + RaiseError(file, line, "Unable to include file '" + includefile + "'. " + e.GetType().Name + ": " + e.Message); + return; + } + + // Remove returns and tabs because the + // parser only uses newline for new lines. + data = data.Replace("\r", ""); + data = data.Replace("\t", ""); + + // Parse the data + IDictionary inc; + if (cs is ListDictionary) inc = new ListDictionary(); else inc = new Hashtable(); + int npos = 0, nline = 1; + InputStructure(inc, ref includefile, ref data, ref npos, ref nline); + if (!cpErrorResult) + { + // Check if a path is given + if ((args.Count > 1) && !string.IsNullOrEmpty(args[1].ToString())) + { + IDictionary def; + if (cs is ListDictionary) def = new ListDictionary(); else def = new Hashtable(); + if (CheckSetting(inc, args[1].ToString(), DEFAULT_SEPERATOR)) + { + inc = (IDictionary)ReadAnySetting(inc, file, line, args[1].ToString(), def, DEFAULT_SEPERATOR); + } + else + { + RaiseError(file, line, "Include missing structure '" + args[1].ToString() + "' in file '" + includefile + "'"); + return; + } + } + + // Recursively merge the structures with the current structure + IDictionary newcs = Combine(cs, inc, (cs is ListDictionary)); + cs.Clear(); + foreach (DictionaryEntry de in newcs) cs.Add(de.Key, de.Value); + } + } + + + // This parses a function + private void ParseFunction(IDictionary cs, ref string file, ref string data, ref int pos, ref int line, ref string functionname) + { + // We now parse arguments, separated by commas, until we reach the end of the function + ArrayList args = new ArrayList(); + object val = null; + while ((pos < data.Length) && !cpErrorResult) + { + // Get current character + char c = data[pos++]; + + // Check for end of function + if (c == ')') + { + // Check what function to run + switch (functionname.Trim().ToLowerInvariant()) + { + // Include another file in here + case "include": + FunctionInclude(cs, args, ref file, line); + return; + + default: + RaiseError(file, line, ERROR_UNKNOWN_FUNCTION); + return; + } + } + // Check for string opening + else if (c == '\"') + { + // Now parsing a string + val = ParseString(ref file, ref data, ref pos, ref line); + if (cpErrorResult) return; + args.Add(val); + } + // Check for numeric character + else if ("0123456789-.&".IndexOf(c.ToString(CultureInfo.InvariantCulture)) > -1) + { + // Go one byte back, because this + // byte is part of the number! + pos--; + + // Now parsing a number + val = ParseNumber(ref file, ref data, ref pos, ref line); + if (cpErrorResult) return; + args.Add(val); + } + // Check for new line + else if (c == '\n') + { + // Count the new line + line++; + } + // Check if argument ends + else if (c == ',') + { + // End of argument + } + // Otherwise (if not whitespace) it is a keyword + else if ((c != ' ') && (c != '\t')) + { + // Go one byte back, because this + // byte is part of the keyword! + pos--; + + // Now parsing a keyword + val = ParseKeyword(ref file, ref data, ref pos, ref line); + if (cpErrorResult) return; + args.Add(val); + } + } + + RaiseError(file, line, ERROR_UNEXPECTED_END); + return; + } + + + // This parses a structure in the given data starting + // from the given pos and line and updates pos and line. + private void InputStructure(IDictionary cs, ref string file, ref string data, ref int pos, ref int line) + { + string key = ""; + + // Go through all of the data until + // the end or until the struct closes + // or when an arror occurred + while ((pos < data.Length) && !cpErrorResult) + { + // Get current character + char c = data[pos++]; + + // Check what character this is + switch (c) + { + case '{': // Begin of new struct + + // Validate key + if (ValidateKey(cs, key.Trim(), file, line)) + { + // Parse this struct and add it + IDictionary cs2; + if (cs is ListDictionary) cs2 = new ListDictionary(); else cs2 = new Hashtable(); + InputStructure(cs2, ref file, ref data, ref pos, ref line); + if (cs.Contains(key.Trim()) && (cs[key.Trim()] is IDictionary)) + cs[key.Trim()] = Combine((IDictionary)cs[key.Trim()], cs2, (cs is ListDictionary)); + else + cs[key.Trim()] = cs2; + key = ""; + } + break; + + case '}': // End of this struct + + // Stop parsing in this struct + return; + + case '(': // Function + ParseFunction(cs, ref file, ref data, ref pos, ref line, ref key); + key = ""; + break; + + case '=': // Assignment + + // Validate key + if (ValidateKey(cs, key.Trim(), file, line)) + { + // Now parsing assignment + object val = ParseAssignment(ref file, ref data, ref pos, ref line); + if (!cpErrorResult) + { + cs[key.Trim()] = val; + key = ""; + } + } + break; + + case ';': // Terminator + + // Validate key + if (!string.IsNullOrEmpty(key)) + { + if (ValidateKey(cs, key.Trim(), file, line)) + { + // Add the key with null as value + cs[key.Trim()] = null; + key = ""; + } + } + break; + + case '\n': // New line + + // Count the line + line++; + + // Add this to the key as a space. + // Spaces are not allowed, but it will be trimmed + // when its the first or last character. + key += " "; + break; + + case '\\': // Possible comment + case '/': + + // Backtrack to use previous character also + pos--; + + // Check for the line comment // + if (data.Substring(pos, 2) == "//") + { + // Find the next line + int np = data.IndexOf("\n", pos); + + // Next line found? + if (np > -1) + { + // Count the line + line++; + + // Skip everything on this line + pos = np + 1; + } + else + { + // No end of line + // Skip everything else + pos = data.Length + 1; + } + } + // Check for the block comment /* */ + else if (data.Substring(pos, 2) == "/*") + { + // Find the next closing block comment + int np = data.IndexOf("*/", pos); + + // Closing block comment found? + if (np > -1) + { + // Count the lines in the block comment + string blockdata = data.Substring(pos, np - pos + 2); + line += (blockdata.Split("\n".ToCharArray()).Length - 1); + + // Skip everything in this block + pos = np + 2; + } + else + { + // No end of line + // Skip everything else + pos = data.Length + 1; + } + } + else + { + // No whitespace + pos++; + } + break; + + default: // Everything else + + // Add character to key + key += c.ToString(CultureInfo.InvariantCulture); + break; + } + } + } + + #endregion + + #region ================== Writing + + // This will create a data structure from the given object + private string OutputStructure(IDictionary cs, int level, string newline, bool whitespace) + { + string leveltabs = ""; + string spacing = ""; + StringBuilder db = new StringBuilder(""); + + // Check if this ConfigStruct is not empty + if (cs.Count > 0) + { + // Create whitespace + if (whitespace) + { + for (int i = 0; i < level; i++) leveltabs += "\t"; + spacing = " "; + } + + // Get enumerator + IDictionaryEnumerator de = cs.GetEnumerator(); + + // Go for each item + for (int i = 0; i < cs.Count; i++) + { + // Go to next item + de.MoveNext(); + + // Check if the value is null + if (de.Value == null) + { + // Output the keyword "null" + //db.Append(leveltabs); db.Append(de.Key.ToString()); db.Append(spacing); + //db.Append("="); db.Append(spacing); db.Append("null;"); db.Append(newline); + + // Output key only + db.Append(leveltabs); db.Append(de.Key.ToString()); db.Append(";"); db.Append(newline); + } + // Check if the value if of ConfigStruct type + else if (de.Value is IDictionary) + { + // Output recursive structure + if (whitespace) { db.Append(leveltabs); db.Append(newline); } + db.Append(leveltabs); db.Append(de.Key); db.Append(newline); + db.Append(leveltabs); db.Append("{"); db.Append(newline); + db.Append(OutputStructure((IDictionary)de.Value, level + 1, newline, whitespace)); + db.Append(leveltabs); db.Append("}"); db.Append(newline); + if (whitespace) { db.Append(leveltabs); db.Append(newline); } + } + // Check if the value is of boolean type + else if (de.Value is bool) + { + // Check value + if ((bool)de.Value == true) + { + // Output the keyword "true" + db.Append(leveltabs); db.Append(de.Key.ToString()); db.Append(spacing); + db.Append("="); db.Append(spacing); db.Append("true;"); db.Append(newline); + } + else + { + // Output the keyword "false" + db.Append(leveltabs); db.Append(de.Key.ToString()); db.Append(spacing); + db.Append("="); db.Append(spacing); db.Append("false;"); db.Append(newline); + } + } + // Check if value is of float type + else if (de.Value is float) + { + // Output the value with a postfixed f + db.Append(leveltabs); db.Append(de.Key); db.Append(spacing); db.Append("="); + db.Append(spacing); db.Append(String.Format(CultureInfo.InvariantCulture, "{0}", de.Value)); db.Append("f;"); db.Append(newline); + } + // Check if value is of other numeric type + else if (de.Value.GetType().IsPrimitive) + { + // Output the value unquoted + db.Append(leveltabs); db.Append(de.Key); db.Append(spacing); db.Append("="); + db.Append(spacing); db.Append(String.Format(CultureInfo.InvariantCulture, "{0}", de.Value)); db.Append(";"); db.Append(newline); + } + else + { + // Output the value with quotes and escape characters + db.Append(leveltabs); db.Append(de.Key); db.Append(spacing); db.Append("="); + db.Append(spacing); db.Append("\""); db.Append(EscapedString(de.Value.ToString())); db.Append("\";"); db.Append(newline); + } + } + } + + // Return the structure + return db.ToString(); + } + + #endregion + + #region ================== Public Methods + + // This clears the last error + public void ClearError() + { + // Clear error + cpErrorResult = false; + cpErrorDescription = ""; + cpErrorLine = 0; + cpErrorFile = ""; + } + + + // This creates a new configuration + public void NewConfiguration() { NewConfiguration(false); } + public void NewConfiguration(bool sorted) + { + // Create new configuration + if (sorted) root = new ListDictionary(); else root = new Hashtable(); + } + + // This checks if a given setting exists (disregards type) + public bool SettingExists(string setting) { return CheckSetting(root, setting, DEFAULT_SEPERATOR); } + public bool SettingExists(string setting, string pathseperator) { return CheckSetting(root, setting, pathseperator); } + + // This can give a value of a key specified in a path form + // also, this does not error when the setting does not exist, + // but instead returns the given default value. + public string ReadSetting(string setting, string defaultsetting) { object r = ReadAnySetting(setting, defaultsetting, DEFAULT_SEPERATOR); if (r != null) return r.ToString(); else return null; } + public string ReadSetting(string setting, string defaultsetting, string pathseperator) { object r = ReadAnySetting(setting, defaultsetting, pathseperator); if (r != null) return r.ToString(); else return null; } + public int ReadSetting(string setting, int defaultsetting) { return Convert.ToInt32(ReadAnySetting(setting, defaultsetting, DEFAULT_SEPERATOR), CultureInfo.InvariantCulture); } + public int ReadSetting(string setting, int defaultsetting, string pathseperator) { return Convert.ToInt32(ReadAnySetting(setting, defaultsetting, pathseperator), CultureInfo.InvariantCulture); } + public float ReadSetting(string setting, float defaultsetting) { return Convert.ToSingle(ReadAnySetting(setting, defaultsetting, DEFAULT_SEPERATOR), CultureInfo.InvariantCulture); } + public float ReadSetting(string setting, float defaultsetting, string pathseperator) { return Convert.ToSingle(ReadAnySetting(setting, defaultsetting, pathseperator), CultureInfo.InvariantCulture); } + public short ReadSetting(string setting, short defaultsetting) { return Convert.ToInt16(ReadAnySetting(setting, defaultsetting, DEFAULT_SEPERATOR), CultureInfo.InvariantCulture); } + public short ReadSetting(string setting, short defaultsetting, string pathseperator) { return Convert.ToInt16(ReadAnySetting(setting, defaultsetting, pathseperator), CultureInfo.InvariantCulture); } + public long ReadSetting(string setting, long defaultsetting) { return Convert.ToInt64(ReadAnySetting(setting, defaultsetting, DEFAULT_SEPERATOR), CultureInfo.InvariantCulture); } + public long ReadSetting(string setting, long defaultsetting, string pathseperator) { return Convert.ToInt64(ReadAnySetting(setting, defaultsetting, pathseperator), CultureInfo.InvariantCulture); } + public bool ReadSetting(string setting, bool defaultsetting) { return Convert.ToBoolean(ReadAnySetting(setting, defaultsetting, DEFAULT_SEPERATOR), CultureInfo.InvariantCulture); } + public bool ReadSetting(string setting, bool defaultsetting, string pathseperator) { return Convert.ToBoolean(ReadAnySetting(setting, defaultsetting, pathseperator), CultureInfo.InvariantCulture); } + public byte ReadSetting(string setting, byte defaultsetting) { return Convert.ToByte(ReadAnySetting(setting, defaultsetting, DEFAULT_SEPERATOR), CultureInfo.InvariantCulture); } + public byte ReadSetting(string setting, byte defaultsetting, string pathseperator) { return Convert.ToByte(ReadAnySetting(setting, defaultsetting, pathseperator), CultureInfo.InvariantCulture); } + public IDictionary ReadSetting(string setting, IDictionary defaultsetting) { return (IDictionary)ReadAnySetting(setting, defaultsetting, DEFAULT_SEPERATOR); } + public IDictionary ReadSetting(string setting, IDictionary defaultsetting, string pathseperator) { return (IDictionary)ReadAnySetting(setting, defaultsetting, pathseperator); } + + public object ReadSettingObject(string setting, object defaultsetting) { return ReadAnySetting(setting, defaultsetting, DEFAULT_SEPERATOR); } + + + // This writes a given setting to the configuration. + // Wont change existing structs, but will add them as needed. + // Returns true when written, false when failed. + public bool WriteSetting(string setting, object settingvalue) { return WriteSetting(setting, settingvalue, DEFAULT_SEPERATOR); } + public bool WriteSetting(string setting, object settingvalue, string pathseperator) + { + IDictionary cs = null; + + // Split the path in an array + string[] keys = setting.Split(pathseperator.ToCharArray()); + string finalkey = keys[keys.Length - 1]; + + // Get the root item + object item = root; + + // Go for each path item + for (int i = 0; i < (keys.Length - 1); i++) + { + // Check if the key is valid + if (ValidateKey(null, keys[i].Trim(), "", -1) == true) + { + // Cast to ConfigStruct + cs = (IDictionary)item; + + // Check if the requested item exists + if (cs.Contains(keys[i]) == true) + { + // Check if the requested item is a ConfigStruct + if (cs[keys[i]] is IDictionary) + { + // Set the item to the next item + item = cs[keys[i]]; + } + else + { + // Cant proceed with path + return false; + } + } + else + { + // Key not found + // Create it now + IDictionary ncs; + if (root is ListDictionary) ncs = new ListDictionary(); else ncs = new Hashtable(); + cs.Add(keys[i], ncs); + + // Set the item to the next item + item = cs[keys[i]]; + } + } + else + { + // Invalid key in path + return false; + } + } + + // Cast to ConfigStruct + cs = (IDictionary)item; + + // Check if the key already exists + if (cs.Contains(finalkey) == true) + { + // Update the value + cs[finalkey] = settingvalue; + } + else + { + // Create the key/value pair + cs.Add(finalkey, settingvalue); + } + + // Return success + return true; + } + + + // This removes a given setting from the configuration. + public bool DeleteSetting(string setting) { return DeleteSetting(setting, DEFAULT_SEPERATOR); } + public bool DeleteSetting(string setting, string pathseperator) + { + IDictionary cs = null; + + // Split the path in an array + string[] keys = setting.Split(pathseperator.ToCharArray()); + string finalkey = keys[keys.Length - 1]; + + // Get the root item + object item = root; + + // Go for each path item + for (int i = 0; i < (keys.Length - 1); i++) + { + // Check if the key is valid + if (ValidateKey(null, keys[i].Trim(), "", -1) == true) + { + // Cast to ConfigStruct + cs = (IDictionary)item; + + // Check if the requested item exists + if (cs.Contains(keys[i]) == true) + { + // Check if the requested item is a ConfigStruct + if (cs[keys[i]] is IDictionary) + { + // Set the item to the next item + item = cs[keys[i]]; + } + else + { + // Cant proceed with path + return false; + } + } + else + { + // Key not found + // Create it now + IDictionary ncs; + if (root is ListDictionary) ncs = new ListDictionary(); else ncs = new Hashtable(); + cs.Add(keys[i], ncs); + + // Set the item to the next item + item = cs[keys[i]]; + } + } + else + { + // Invalid key in path + return false; + } + } + + // Cast to ConfigStruct + cs = (IDictionary)item; + + // Arrived at our destination + // Delete the key if the key exists + if (cs.Contains(finalkey) == true) + { + // Key exists, delete it + cs.Remove(finalkey); + + // Return success + return true; + } + else + { + // Key not found, return fail + return false; + } + } + + + // This will save the current configuration to the specified file + public bool SaveConfiguration(string filename) { return SaveConfiguration(filename, "\r\n", true); } + public bool SaveConfiguration(string filename, string newline) { return SaveConfiguration(filename, newline, true); } + public bool SaveConfiguration(string filename, string newline, bool whitespace) + { + // Kill the file if it exists + if (File.Exists(filename) == true) File.Delete(filename); + + // Open file stream for writing + FileStream fstream = File.OpenWrite(filename); + + // Create output structure and write to file + string data = OutputConfiguration(newline, whitespace); + byte[] baData = Encoding.UTF8.GetBytes(data); + fstream.Write(baData, 0, baData.Length); + fstream.Flush(); + fstream.Close(); + + // Return true when done, false when errors occurred + return !cpErrorResult; + } + + + // This will output the current configuration as a string + public string OutputConfiguration() { return OutputConfiguration("\r\n", true); } + public string OutputConfiguration(string newline) { return OutputConfiguration(newline, true); } + public string OutputConfiguration(string newline, bool whitespace) + { + // Simply return the configuration structure as string + return OutputStructure(root, 0, newline, whitespace); + } + + + // This will load a configuration from file + public bool LoadConfiguration(string filename) { return LoadConfiguration(filename, false); } + public bool LoadConfiguration(string filename, bool sorted) + { + // Check if the file is missing + if (File.Exists(filename) == false) + { + throw (new FileNotFoundException("File not found \"" + filename + "\"", filename)); + } + else + { + // Load the file contents + FileStream fstream = File.OpenRead(filename); + byte[] fbuffer = new byte[fstream.Length]; + fstream.Read(fbuffer, 0, fbuffer.Length); + fstream.Close(); + + // Convert byte array to string + string data = Encoding.UTF8.GetString(fbuffer); + + // Load the configuration from this data + return InputConfiguration(filename, data, sorted); + } + } + + + // This will load a configuration from string + public bool InputConfiguration(string data) { return InputConfiguration(data, false); } + public bool InputConfiguration(string data, bool sorted) { return InputConfiguration("", data, sorted); } + private bool InputConfiguration(string file, string data, bool sorted) + { + // Remove returns and tabs because the + // parser only uses newline for new lines. + data = data.Replace("\r", ""); + data = data.Replace("\t", ""); + + // Clear errors + ClearError(); + + // Parse the data to the root structure + if (sorted) root = new ListDictionary(); else root = new Hashtable(); + int pos = 0, line = 1; + InputStructure(root, ref file, ref data, ref pos, ref line); + + // Return true when done, false when errors occurred + return !cpErrorResult; + } + + #endregion + } +} diff --git a/Source/Core/IO/DeserializerStream.cs b/Source/Core/IO/DeserializerStream.cs new file mode 100644 index 0000000..0ea7b7d --- /dev/null +++ b/Source/Core/IO/DeserializerStream.cs @@ -0,0 +1,231 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using CodeImp.DoomBuilder.Geometry; +using CodeImp.DoomBuilder.Rendering; +using SlimDX.Direct3D9; +using System.Drawing; +using System.IO; +using CodeImp.DoomBuilder.Map; // villsa + +#endregion + +namespace CodeImp.DoomBuilder.IO +{ + internal sealed class DeserializerStream : IReadWriteStream + { + #region ================== Constants + + #endregion + + #region ================== Variables + + private Stream stream; + private BinaryReader reader; + private string[] stringstable; + private int stringtablepos; + + #endregion + + #region ================== Properties + + public bool IsWriting { get { return false; } } + + public int EndPosition { get { return stringtablepos; } } + + #endregion + + #region ================== Constructor / Destructor + + // Constructor + public DeserializerStream(Stream stream) + { + // Initialize + this.stream = stream; + this.reader = new BinaryReader(stream); + } + + #endregion + + #region ================== Methods + + // Management + public void Begin() + { + // First 4 bytes are reserved for the offset of the strings table + stringtablepos = reader.ReadInt32(); + stream.Seek(stringtablepos, SeekOrigin.Begin); + + // Read the strings + List strings = new List(); + while (stream.Position < (int)stream.Length) + strings.Add(reader.ReadString()); + stringstable = strings.ToArray(); + + // Back to start + stream.Seek(4, SeekOrigin.Begin); + } + + public void End() + { + } + + // Bidirectional + public void rwInt(ref int v) { v = reader.ReadInt32(); } + + public void rwByte(ref byte v) { v = reader.ReadByte(); } + + public void rwShort(ref short v) { v = reader.ReadInt16(); } + + public void rwString(ref string v) + { + ushort index = reader.ReadUInt16(); + v = stringstable[index]; + } + + public void rwLong(ref long v) { v = reader.ReadInt64(); } + + public void rwUInt(ref uint v) { v = reader.ReadUInt32(); } + + public void rwUShort(ref ushort v) { v = reader.ReadUInt16(); } + + public void rwULong(ref ulong v) { v = reader.ReadUInt64(); } + + public void rwFloat(ref float v) { v = reader.ReadSingle(); } + + public void rwBool(ref bool v) { v = reader.ReadBoolean(); } + + public void rwVector2D(ref Vector2D v) + { + v.x = reader.ReadSingle(); + v.y = reader.ReadSingle(); + } + + public void rwVector3D(ref Vector3D v) + { + v.x = reader.ReadSingle(); + v.y = reader.ReadSingle(); + v.z = reader.ReadSingle(); + } + + //villsa + public void rwLight(ref Lights v) + { + v.color.r = reader.ReadByte(); + v.color.g = reader.ReadByte(); + v.color.b = reader.ReadByte(); + v.color.a = reader.ReadByte(); + v.tag = reader.ReadUInt16(); + } + + // Write-only is not supported + public void wInt(int v) { General.Fail("Write-only is not supported on deserialization stream. Consider passing the element by reference for bidirectional support."); } + + public void wByte(byte v) { General.Fail("Write-only is not supported on deserialization stream. Consider passing the element by reference for bidirectional support."); } + + public void wShort(short v) { General.Fail("Write-only is not supported on deserialization stream. Consider passing the element by reference for bidirectional support."); } + + public void wString(string v) { General.Fail("Write-only is not supported on deserialization stream. Consider passing the element by reference for bidirectional support."); } + + public void wLong(long v) { General.Fail("Write-only is not supported on deserialization stream. Consider passing the element by reference for bidirectional support."); } + + public void wUInt(uint v) { General.Fail("Write-only is not supported on deserialization stream. Consider passing the element by reference for bidirectional support."); } + + public void wUShort(ushort v) { General.Fail("Write-only is not supported on deserialization stream. Consider passing the element by reference for bidirectional support."); } + + public void wULong(ulong v) { General.Fail("Write-only is not supported on deserialization stream. Consider passing the element by reference for bidirectional support."); } + + public void wFloat(float v) { General.Fail("Write-only is not supported on deserialization stream. Consider passing the element by reference for bidirectional support."); } + + public void wBool(bool v) { General.Fail("Write-only is not supported on deserialization stream. Consider passing the element by reference for bidirectional support."); } + + public void wVector2D(Vector2D v) + { + General.Fail("Write-only is not supported on deserialization stream. Consider passing the element by reference for bidirectional support."); + } + + public void wVector3D(Vector3D v) + { + General.Fail("Write-only is not supported on deserialization stream. Consider passing the element by reference for bidirectional support."); + } + + // villsa + public void wLight(Lights v) + { + General.Fail("Write-only is not supported on deserialization stream. Consider passing the element by reference for bidirectional support."); + } + + // Read-only + public void rInt(out int v) { v = reader.ReadInt32(); } + + public void rByte(out byte v) { v = reader.ReadByte(); } + + public void rShort(out short v) { v = reader.ReadInt16(); } + + public void rString(out string v) + { + ushort index = reader.ReadUInt16(); + v = stringstable[index]; + } + + public void rLong(out long v) { v = reader.ReadInt64(); } + + public void rUInt(out uint v) { v = reader.ReadUInt32(); } + + public void rUShort(out ushort v) { v = reader.ReadUInt16(); } + + public void rULong(out ulong v) { v = reader.ReadUInt64(); } + + public void rFloat(out float v) { v = reader.ReadSingle(); } + + public void rBool(out bool v) { v = reader.ReadBoolean(); } + + public void rVector2D(out Vector2D v) + { + v = new Vector2D(); + v.x = reader.ReadSingle(); + v.y = reader.ReadSingle(); + } + + public void rVector3D(out Vector3D v) + { + v = new Vector3D(); + v.x = reader.ReadSingle(); + v.y = reader.ReadSingle(); + v.z = reader.ReadSingle(); + } + + //villsa + public void rLight(out Lights v) + { + v = new Lights(); + v.color.r = reader.ReadByte(); + v.color.g = reader.ReadByte(); + v.color.b = reader.ReadByte(); + v.color.a = reader.ReadByte(); + v.tag = reader.ReadUInt16(); + } + + #endregion + } +} diff --git a/Source/Core/IO/DirectoryFileEntry.cs b/Source/Core/IO/DirectoryFileEntry.cs new file mode 100644 index 0000000..6b578e8 --- /dev/null +++ b/Source/Core/IO/DirectoryFileEntry.cs @@ -0,0 +1,90 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.IO; + +#endregion + +namespace CodeImp.DoomBuilder.IO +{ + internal struct DirectoryFileEntry + { + // Example for: C:\WADs\Foo\Bar.WAD + // Created from: C:\WADs + // Members + public string filename; // bar.wad + public string filetitle; // bar + public string extension; // wad + public string path; // foo + public string filepathname; // Foo\Bar.WAD + public string filepathtitle; // Foo\Bar + + // Constructor + public DirectoryFileEntry(string fullname, string frompath) + { + // Get the information we need + filename = Path.GetFileName(fullname); + filetitle = Path.GetFileNameWithoutExtension(fullname); + extension = Path.GetExtension(fullname); + if (extension.Length > 1) + extension = extension.Substring(1); + else + extension = ""; + path = Path.GetDirectoryName(fullname); + if (path.Length > (frompath.Length + 1)) + path = path.Substring(frompath.Length + 1); + else + path = ""; + filepathname = Path.Combine(path, filename); + filepathtitle = Path.Combine(path, filetitle); + + // Make some lowercase + filename = filename.ToLowerInvariant(); + filetitle = filetitle.ToLowerInvariant(); + extension = extension.ToLowerInvariant(); + path = path.ToLowerInvariant(); + } + + // Constructor + public DirectoryFileEntry(string fullname) + { + // Get the information we need + filename = Path.GetFileName(fullname); + filetitle = Path.GetFileNameWithoutExtension(fullname); + extension = Path.GetExtension(fullname); + if (extension.Length > 1) + extension = extension.Substring(1); + else + extension = ""; + path = Path.GetDirectoryName(fullname); + filepathname = Path.Combine(path, filename); + filepathtitle = Path.Combine(path, filetitle); + + // Make some lowercase + filename = filename.ToLowerInvariant(); + filetitle = filetitle.ToLowerInvariant(); + extension = extension.ToLowerInvariant(); + path = path.ToLowerInvariant(); + } + } +} diff --git a/Source/Core/IO/DirectoryFilesList.cs b/Source/Core/IO/DirectoryFilesList.cs new file mode 100644 index 0000000..7da68f0 --- /dev/null +++ b/Source/Core/IO/DirectoryFilesList.cs @@ -0,0 +1,301 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.IO; + +#endregion + +namespace CodeImp.DoomBuilder.IO +{ + internal sealed class DirectoryFilesList + { + #region ================== Variables + + private DirectoryFileEntry[] entries; + private Dictionary hashedentries; + + #endregion + + #region ================== Properties + + public int Count { get { return entries.Length; } } + + #endregion + + #region ================== Constructor / Disposer + + // Constructor to fill list from directory and optionally subdirectories + public DirectoryFilesList(string path, bool subdirectories) + { + path = Path.GetFullPath(path); + string[] files = Directory.GetFiles(path, "*", subdirectories ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly); + entries = new DirectoryFileEntry[files.Length]; + hashedentries = new Dictionary(files.Length); + for (int i = 0; i < files.Length; i++) + { + entries[i] = new DirectoryFileEntry(files[i], path); + string hashkey = entries[i].filepathname.ToLowerInvariant(); + if (hashedentries.ContainsKey(hashkey)) + throw new IOException("Multiple files with the same filename in the same directory are not allowed. See: \"" + entries[i].filepathname + "\""); + hashedentries.Add(hashkey, entries[i]); + } + } + + // Constructor for custom list + public DirectoryFilesList(ICollection sourceentries) + { + int index = 0; + entries = new DirectoryFileEntry[sourceentries.Count]; + hashedentries = new Dictionary(sourceentries.Count); + foreach (DirectoryFileEntry e in sourceentries) + { + entries[index] = e; + string hashkey = e.filepathname.ToLowerInvariant(); + if (hashedentries.ContainsKey(hashkey)) + throw new IOException("Multiple files with the same filename in the same directory are not allowed. See: \"" + e.filepathname + "\""); + hashedentries.Add(hashkey, e); + index++; + } + } + + #endregion + + #region ================== Methods + + // This checks if a given file exists + // The given file path must not be absolute + public bool FileExists(string filepathname) + { + return hashedentries.ContainsKey(filepathname.ToLowerInvariant()); + } + + // This returns file information for the given file + // The given file path must not be absolute + public DirectoryFileEntry GetFileInfo(string filepathname) + { + return hashedentries[filepathname.ToLowerInvariant()]; + } + + // This returns a list of all files (filepathname) + public List GetAllFiles() + { + List files = new List(entries.Length); + for (int i = 0; i < entries.Length; i++) files.Add(entries[i].filepathname); + return files; + } + + // This returns a list of all files optionally with subdirectories included + public List GetAllFiles(bool subdirectories) + { + if (subdirectories) + return GetAllFiles(); + else + { + List files = new List(entries.Length); + for (int i = 0; i < entries.Length; i++) + if (entries[i].path.Length == 0) files.Add(entries[i].filepathname); + return files; + } + } + + // This returns a list of all files that are in the given path and optionally in subdirectories + public List GetAllFiles(string path, bool subdirectories) + { + path = CorrectPath(path).ToLowerInvariant(); + if (subdirectories) + { + List files = new List(entries.Length); + for (int i = 0; i < entries.Length; i++) + if (entries[i].path.StartsWith(path)) files.Add(entries[i].filepathname); + return files; + } + else + { + List files = new List(entries.Length); + for (int i = 0; i < entries.Length; i++) + if (entries[i].path == path) files.Add(entries[i].filepathname); + return files; + } + } + + // This returns a list of all files that are in the given path and subdirectories and have the given title + public List GetAllFilesWithTitle(string path, string title) + { + path = CorrectPath(path).ToLowerInvariant(); + title = title.ToLowerInvariant(); + List files = new List(entries.Length); + for (int i = 0; i < entries.Length; i++) + if (entries[i].path.StartsWith(path) && (entries[i].filetitle == title)) + files.Add(entries[i].filepathname); + return files; + } + + // This returns a list of all files that are in the given path (optionally in subdirectories) and have the given title + public List GetAllFilesWithTitle(string path, string title, bool subdirectories) + { + if (subdirectories) + return GetAllFilesWithTitle(path, title); + else + { + path = CorrectPath(path).ToLowerInvariant(); + title = title.ToLowerInvariant(); + List files = new List(entries.Length); + for (int i = 0; i < entries.Length; i++) + if ((entries[i].path == path) && (entries[i].filetitle == title)) + files.Add(entries[i].filepathname); + return files; + } + } + + // This returns a list of all files that are in the given path and subdirectories and have the given extension + public List GetAllFiles(string path, string extension) + { + path = CorrectPath(path).ToLowerInvariant(); + extension = extension.ToLowerInvariant(); + List files = new List(entries.Length); + for (int i = 0; i < entries.Length; i++) + if (entries[i].path.StartsWith(path) && (entries[i].extension == extension)) + files.Add(entries[i].filepathname); + return files; + } + + // This returns a list of all files that are in the given path (optionally in subdirectories) and have the given extension + public List GetAllFiles(string path, string extension, bool subdirectories) + { + if (subdirectories) + return GetAllFiles(path, extension); + else + { + path = CorrectPath(path).ToLowerInvariant(); + extension = extension.ToLowerInvariant(); + List files = new List(entries.Length); + for (int i = 0; i < entries.Length; i++) + if ((entries[i].path == path) && (entries[i].extension == extension)) + files.Add(entries[i].filepathname); + return files; + } + } + + // This finds the first file that has the specified name, regardless of file extension + public string GetFirstFile(string title, bool subdirectories) + { + title = title.ToLowerInvariant(); + if (subdirectories) + { + for (int i = 0; i < entries.Length; i++) + if (entries[i].filetitle == title) + return entries[i].filepathname; + } + else + { + for (int i = 0; i < entries.Length; i++) + if ((entries[i].filetitle == title) && (entries[i].path.Length == 0)) + return entries[i].filepathname; + } + + return null; + } + + // This finds the first file that has the specified name and extension + public string GetFirstFile(string title, bool subdirectories, string extension) + { + title = title.ToLowerInvariant(); + extension = extension.ToLowerInvariant(); + if (subdirectories) + { + for (int i = 0; i < entries.Length; i++) + if ((entries[i].filetitle == title) && (entries[i].extension == extension)) + return entries[i].filepathname; + } + else + { + for (int i = 0; i < entries.Length; i++) + if ((entries[i].filetitle == title) && (entries[i].path.Length == 0) && (entries[i].extension == extension)) + return entries[i].filepathname; + } + + return null; + } + + // This finds the first file that has the specified name, regardless of file extension, and is in the given path + public string GetFirstFile(string path, string title, bool subdirectories) + { + title = title.ToLowerInvariant(); + path = CorrectPath(path).ToLowerInvariant(); + if (subdirectories) + { + for (int i = 0; i < entries.Length; i++) + if ((entries[i].filetitle == title) && entries[i].path.StartsWith(path)) + return entries[i].filepathname; + } + else + { + for (int i = 0; i < entries.Length; i++) + if ((entries[i].filetitle == title) && (entries[i].path == path)) + return entries[i].filepathname; + } + + return null; + } + + // This finds the first file that has the specified name, is in the given path and has the given extension + public string GetFirstFile(string path, string title, bool subdirectories, string extension) + { + title = title.ToLowerInvariant(); + path = CorrectPath(path).ToLowerInvariant(); + extension = extension.ToLowerInvariant(); + if (subdirectories) + { + for (int i = 0; i < entries.Length; i++) + if ((entries[i].filetitle == title) && entries[i].path.StartsWith(path) && (entries[i].extension == extension)) + return entries[i].filepathname; + } + else + { + for (int i = 0; i < entries.Length; i++) + if ((entries[i].filetitle == title) && (entries[i].path == path) && (entries[i].extension == extension)) + return entries[i].filepathname; + } + + return null; + } + + // This fixes a path so that it doesn't have the \ at the end + private string CorrectPath(string path) + { + if (path.Length > 0) + { + if ((path[path.Length - 1] == Path.DirectorySeparatorChar) || (path[path.Length - 1] == Path.AltDirectorySeparatorChar)) + return path.Substring(0, path.Length - 1); + else + return path; + } + else + { + return path; + } + } + + #endregion + } +} diff --git a/Source/Core/IO/Doom64MapSetIO.cs b/Source/Core/IO/Doom64MapSetIO.cs new file mode 100644 index 0000000..178d75e --- /dev/null +++ b/Source/Core/IO/Doom64MapSetIO.cs @@ -0,0 +1,1251 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.IO; +using System.Drawing; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Geometry; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Config; // villsa + +#endregion + +namespace CodeImp.DoomBuilder.IO +{ + internal class Doom64MapSetIO : MapSetIO + { + #region ================== Constants + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + public Doom64MapSetIO(WAD wad, MapManager manager) : base(wad, manager) + { + } + + #endregion + + #region ================== Variables + + internal List light; + + #endregion + + #region ================== Properties + + public override int MaxSidedefs { get { return ushort.MaxValue; } } + public override int MaxVertices { get { return ushort.MaxValue; } } + public override int MaxLinedefs { get { return ushort.MaxValue; } } + public override int MaxSectors { get { return ushort.MaxValue; } } + public override int MaxThings { get { return ushort.MaxValue; } } + public override int MinTextureOffset { get { return short.MinValue; } } + public override int MaxTextureOffset { get { return short.MaxValue; } } + public override int VertexDecimals { get { return 0; } } + public override string DecimalsFormat { get { return "0"; } } + public override bool HasLinedefTag { get { return true; } } + public override bool HasThingTag { get { return true; } } + public override bool HasThingAction { get { return false; } } + public override bool HasCustomFields { get { return false; } } + public override bool HasThingHeight { get { return true; } } + public override bool HasActionArgs { get { return false; } } + public override bool HasMixedActivations { get { return false; } } + public override bool HasPresetActivations { get { return false; } } + public override bool HasBuiltInActivations { get { return false; } } + public override bool HasNumericLinedefFlags { get { return true; } } + public override bool HasNumericThingFlags { get { return true; } } + public override bool HasNumericLinedefActivations { get { return true; } } + public override int MaxTag { get { return ushort.MaxValue; } } + public override int MinTag { get { return ushort.MinValue; } } + public override int MaxAction { get { return 511; } } + public override int MinAction { get { return ushort.MinValue; } } + public override int MaxArgument { get { return 0; } } + public override int MinArgument { get { return 0; } } + public override int MaxEffect { get { return ushort.MaxValue; } } + public override int MinEffect { get { return ushort.MinValue; } } + public override int MaxBrightness { get { return short.MaxValue; } } + public override int MinBrightness { get { return short.MinValue; } } + public override int MaxThingType { get { return ushort.MaxValue; } } + public override int MinThingType { get { return ushort.MinValue; } } + public override double MaxCoordinate { get { return (double)short.MaxValue; } } + public override double MinCoordinate { get { return (double)short.MinValue; } } + public override bool InDoom64Mode { get { return true; } } // villsa + + #endregion + + #region ================== Light Functions + + internal void AddLight(Sector s, Lights slight) + { + Lights l; + + l = slight; + if (s.Tag != 0) + l.tag = (UInt16)s.Tag; + slight = l; + + if (slight.color.r == slight.color.g && + slight.color.r == slight.color.b && + slight.tag == 0) + return; + + if (!light.Contains(slight)) + light.Add(slight); + } + + internal UInt16 GetLight(Sector s, Lights slight) + { + UInt16 i = 0; + Lights lgt; + + lgt = slight; + lgt.tag = (UInt16)s.Tag; + slight = lgt; + + if (slight.color.r == slight.color.g && + slight.color.r == slight.color.b) + { + return slight.color.r; + } + + foreach (Lights l in light) + { + if (l.color.r == slight.color.r && + l.color.g == slight.color.g && + l.color.b == slight.color.b && + l.tag == slight.tag) + { + return (UInt16)(256 + i); + } + i++; + } + + return 0; + } + + #endregion + + #region ================== Reading + + // This reads a map from the file and returns a MapSet + public override MapSet Read(MapSet map, string mapname) + { + int firstindex; + Dictionary vertexlink; + Dictionary sectorlink; + + //ReadMapInfo(map, mapname); // DON'T USE + + // Find the index where first map lump begins + firstindex = wad.FindLumpIndex(mapname) + 1; + + // Read vertices + vertexlink = ReadVertices(map, firstindex); + + // Read sectors + sectorlink = ReadSectors(map, firstindex); + + // Read linedefs and sidedefs + ReadLinedefs(map, firstindex, vertexlink, sectorlink); + + // Read things + ReadThings(map, firstindex); + + // Read macros + //ReadMacros(map, firstindex); + + // Remove unused vertices + map.RemoveUnusedVertices(); + + // Return result; + return map; + } + + // This reads the map info header from WAD file + /*private void ReadMapInfo(MapSet map, string mapname) + { + MemoryStream mem; + BinaryReader reader; + PixelColor color; + + if (wad.Lumps[wad.FindLumpIndex(mapname)].Length <= 0) + return; + + Lump lump = wad.FindLump(mapname); + mem = new MemoryStream(lump.Stream.ReadAllBytes()); + reader = new BinaryReader(mem); + + General.Map.Map.MapInfo.MapName = General.Map.Map.MapInfo.ReadString(reader); + + for (int i = 0; i < MapInfo.INTERMISSION_LINES; i++) + map.MapInfo.IntermissionText[i] = map.MapInfo.ReadString(reader); + + General.Map.Map.MapInfo.OverrideSky = reader.ReadBoolean(); + General.Map.Map.MapInfo.Finale = reader.ReadBoolean(); + General.Map.Map.MapInfo.Type = reader.ReadByte(); + + color = new PixelColor(); + + color.b = reader.ReadByte(); + color.g = reader.ReadByte(); + color.r = reader.ReadByte(); + color.a = reader.ReadByte(); + + General.Map.Map.MapInfo.FogColor = color; + + color.b = reader.ReadByte(); + color.g = reader.ReadByte(); + color.r = reader.ReadByte(); + color.a = reader.ReadByte(); + + General.Map.Map.MapInfo.TopSkyColor = color; + + color.b = reader.ReadByte(); + color.g = reader.ReadByte(); + color.r = reader.ReadByte(); + color.a = reader.ReadByte(); + + General.Map.Map.MapInfo.LowerSkyColor = color; + + General.Map.Map.MapInfo.MusicID = reader.ReadUInt16(); + + // Done + mem.Dispose(); + }*/ + + // This reads the THINGS from WAD file + private void ReadThings(MapSet map, int firstindex) + { + MemoryStream mem; + BinaryReader reader; + int num, i, tag, z, x, y, type, flags; + Dictionary stringflags; + float angle; + Thing t; + + // Get the lump from wad file + Lump lump = wad.FindLump("THINGS", firstindex); + if (lump == null) throw new Exception("Could not find required lump THINGS!"); + + // Prepare to read the items + mem = new MemoryStream(lump.Stream.ReadAllBytes()); + num = (int)lump.Stream.Length / 14; + reader = new BinaryReader(mem); + + // Read items from the lump + map.SetCapacity(0, 0, 0, 0, map.Things.Count + num); + for (i = 0; i < num; i++) + { + // Read properties from stream + x = reader.ReadInt16(); + y = reader.ReadInt16(); + z = reader.ReadInt16(); + angle = Angle2D.DoomToReal(reader.ReadInt16()); + type = reader.ReadUInt16(); + flags = reader.ReadUInt16(); + tag = reader.ReadUInt16(); + + // Make string flags + stringflags = new Dictionary(); + foreach (KeyValuePair f in manager.Config.ThingFlags) + { + int fnum; + if (int.TryParse(f.Key, out fnum)) stringflags[f.Key] = ((flags & fnum) == fnum); + } + + // Create new item + t = map.CreateThing(); + t.Update(type, x, y, z, angle, stringflags, tag, 0, new int[Thing.NUM_ARGS]); + } + + // Done + mem.Dispose(); + } + + // This reads the VERTICES from WAD file + // Returns a lookup table with indices + private Dictionary ReadVertices(MapSet map, int firstindex) + { + MemoryStream mem; + Dictionary link; + BinaryReader reader; + int num, i, x, y; + Vertex v; + + // Get the lump from wad file + Lump lump = wad.FindLump("VERTEXES", firstindex); + if (lump == null) throw new Exception("Could not find required lump VERTEXES!"); + + // Prepare to read the items + mem = new MemoryStream(lump.Stream.ReadAllBytes()); + num = (int)lump.Stream.Length / 8; + reader = new BinaryReader(mem); + + // Create lookup table + link = new Dictionary(num); + + // Read items from the lump + map.SetCapacity(map.Vertices.Count + num, 0, 0, 0, 0); + for (i = 0; i < num; i++) + { + // Read properties from stream + x = reader.ReadInt32() / 65536; + y = reader.ReadInt32() / 65536; + + // Create new item + v = map.CreateVertex(new Vector2D((float)x, (float)y)); + + // Add it to the lookup table + link.Add(i, v); + } + + // Done + mem.Dispose(); + + // Return lookup table + return link; + } + + // This reads the SECTORS from WAD file + // Returns a lookup table with indices + private Dictionary ReadSectors(MapSet map, int firstindex) + { + MemoryStream mem; + MemoryStream lightmem; + Dictionary link; + BinaryReader reader; + BinaryReader lightreader; + int num, i, hfloor, hceil, special, tag, flags; + int lightnum; + int[] colors = new int[Sector.NUM_COLORS]; + uint tfloor, tceil; + Lights[] lightColors; + Dictionary stringflags; + Sector s; + string fname = "-"; + string cname = "-"; + + // Get the lump from wad file + Lump lump = wad.FindLump("SECTORS", firstindex); + if (lump == null) throw new Exception("Could not find required lump SECTORS!"); + + // Get the lights lump from wad file + Lump lightlump = wad.FindLump("LIGHTS", firstindex); + if (lightlump == null) throw new Exception("Could not find required lump LIGHTS!"); + + // Prepare to read the items + mem = new MemoryStream(lump.Stream.ReadAllBytes()); + lightmem = new MemoryStream(lightlump.Stream.ReadAllBytes()); + num = (int)lump.Stream.Length / 24; + lightnum = (int)lightlump.Stream.Length / 6; + reader = new BinaryReader(mem); + lightreader = new BinaryReader(lightmem); + + // Create lookup table + link = new Dictionary(num); + + // Get light table + lightColors = new Lights[lightnum]; + for (i = 0; i < lightnum; i++) + { + lightColors[i].color.r = lightreader.ReadByte(); + lightColors[i].color.g = lightreader.ReadByte(); + lightColors[i].color.b = lightreader.ReadByte(); + lightColors[i].color.a = lightreader.ReadByte(); + lightColors[i].tag = lightreader.ReadUInt16(); + } + + // Read items from the lump + map.SetCapacity(0, 0, 0, map.Sectors.Count + num, 0); + for (i = 0; i < num; i++) + { + // Read properties from stream + hfloor = reader.ReadInt16(); + hceil = reader.ReadInt16(); + tfloor = reader.ReadUInt16(); + tceil = reader.ReadUInt16(); + colors[0] = reader.ReadUInt16(); + colors[1] = reader.ReadUInt16(); + colors[2] = reader.ReadUInt16(); + colors[3] = reader.ReadUInt16(); + colors[4] = reader.ReadUInt16(); + special = reader.ReadUInt16(); + tag = reader.ReadInt16(); + flags = reader.ReadUInt16(); + + // Make string flags + stringflags = new Dictionary(); + foreach (string f in manager.Config.SortedSectorFlags) + { + int fnum; + if (int.TryParse(f, out fnum)) stringflags[f] = ((flags & fnum) == fnum); + } + + // Create new item + s = map.CreateSector(); + + s.HashFloor = tfloor; + s.HashCeiling = tceil; + + s.Update(stringflags, hfloor, hceil, fname, cname, special, tag, lightColors, colors); + + // Add it to the lookup table + link.Add(i, s); + } + + // Done + mem.Dispose(); + lightmem.Dispose(); + + // Return lookup table + return link; + } + + // This reads the LINEDEFS and SIDEDEFS from WAD file + private void ReadLinedefs(MapSet map, int firstindex, + Dictionary vertexlink, Dictionary sectorlink) + { + MemoryStream linedefsmem, sidedefsmem; + BinaryReader readline, readside; + Lump linedefslump, sidedefslump; + int num, numsides, i, offsetx, offsety, v1, v2; + int s1, s2, action, sc, tag; + uint flags; + int switchmask; + Dictionary stringflags; + uint thigh, tmid, tlow; + Linedef l; + Sidedef s; + string hname = "-"; + string lname = "-"; + string mname = "-"; + + // Get the linedefs lump from wad file + linedefslump = wad.FindLump("LINEDEFS", firstindex); + if (linedefslump == null) throw new Exception("Could not find required lump LINEDEFS!"); + + // Get the sidedefs lump from wad file + sidedefslump = wad.FindLump("SIDEDEFS", firstindex); + if (sidedefslump == null) throw new Exception("Could not find required lump SIDEDEFS!"); + + // Prepare to read the items + linedefsmem = new MemoryStream(linedefslump.Stream.ReadAllBytes()); + sidedefsmem = new MemoryStream(sidedefslump.Stream.ReadAllBytes()); + num = (int)linedefslump.Stream.Length / 16; + numsides = (int)sidedefslump.Stream.Length / 12; + readline = new BinaryReader(linedefsmem); + readside = new BinaryReader(sidedefsmem); + + // Read items from the lump + map.SetCapacity(0, map.Linedefs.Count + num, map.Sidedefs.Count + numsides, 0, 0); + for (i = 0; i < num; i++) + { + // Read properties from stream + v1 = readline.ReadUInt16(); + v2 = readline.ReadUInt16(); + flags = readline.ReadUInt32(); + action = readline.ReadUInt16(); + tag = readline.ReadInt16(); + s1 = readline.ReadUInt16(); + s2 = readline.ReadUInt16(); + + switchmask = 0; + + if ((flags & 0x2000) == 0x2000) + switchmask |= 0x2000; + + if ((flags & 0x4000) == 0x4000) + switchmask |= 0x4000; + + if ((flags & 0x8000) == 0x8000) + switchmask |= 0x8000; + + // Make string flags + stringflags = new Dictionary(); + foreach (string f in manager.Config.SortedLinedefFlags) + { + uint fnum; + if (uint.TryParse(f, out fnum)) stringflags[f] = ((flags & fnum) == fnum); + } + + // Create new linedef + if (vertexlink.ContainsKey(v1) && vertexlink.ContainsKey(v2)) + { + // Check if not zero-length + if (Vector2D.ManhattanDistance(vertexlink[v1].Position, vertexlink[v2].Position) > 0.0001f) + { + l = map.CreateLinedef(vertexlink[v1], vertexlink[v2]); + l.Update(stringflags, action, tag, action & 511, switchmask, new int[Linedef.NUM_ARGS]); + l.UpdateCache(); + + // Line has a front side? + if (s1 != ushort.MaxValue) + { + // Read front sidedef + sidedefsmem.Seek(s1 * 12, SeekOrigin.Begin); + if ((s1 * 12L) <= (sidedefsmem.Length - 12L)) + { + offsetx = readside.ReadInt16(); + offsety = readside.ReadInt16(); + thigh = readside.ReadUInt16(); + tlow = readside.ReadUInt16(); + tmid = readside.ReadUInt16(); + sc = readside.ReadUInt16(); + + // Create front sidedef + if (sectorlink.ContainsKey(sc)) + { + /*for (int j = 0; j < General.Map.TextureHashKey.Count; j++) + { + if (thigh == General.Map.TextureHashKey[j]) + { + if (j == 0) + { + hname = "-"; + break; + } + + hname = General.Map.TextureHashName[j]; + break; + } + } + + for (int j = 0; j < General.Map.TextureHashKey.Count; j++) + { + if (tlow == General.Map.TextureHashKey[j]) + { + if (j == 0) + { + lname = "-"; + break; + } + + lname = General.Map.TextureHashName[j]; + break; + } + } + + for (int j = 0; j < General.Map.TextureHashKey.Count; j++) + { + if (tmid == General.Map.TextureHashKey[j]) + { + if (j == 0) + { + mname = "-"; + break; + } + + mname = General.Map.TextureHashName[j]; + break; + } + }*/ + + s = map.CreateSidedef(l, true, sectorlink[sc]); + + s.HashTexHigh = thigh; + s.HashTexMid = tmid; + s.HashTexLow = tlow; + + s.Update(offsetx, offsety, hname, mname, lname); + //General.Map.Config.D64TextureIndex[thigh].Title, + //General.Map.Config.D64TextureIndex[tmid].Title, + //General.Map.Config.D64TextureIndex[tlow].Title); + } + else + { + General.ErrorLogger.Add(ErrorType.Warning, "Sidedef " + s1 + " references invalid sector " + sc + ". Sidedef has been removed."); + } + } + else + { + General.ErrorLogger.Add(ErrorType.Warning, "Linedef " + i + " references invalid sidedef " + s1 + ". Sidedef has been removed."); + } + } + + // Line has a back side? + if (s2 != ushort.MaxValue) + { + // Read back sidedef + sidedefsmem.Seek(s2 * 12, SeekOrigin.Begin); + if ((s2 * 12L) <= (sidedefsmem.Length - 12L)) + { + offsetx = readside.ReadInt16(); + offsety = readside.ReadInt16(); + thigh = readside.ReadUInt16(); + tlow = readside.ReadUInt16(); + tmid = readside.ReadUInt16(); + sc = readside.ReadUInt16(); + + // Create back sidedef + if (sectorlink.ContainsKey(sc)) + { + /*for (int j = 0; j < General.Map.TextureHashKey.Count; j++) + { + if (thigh == General.Map.TextureHashKey[j]) + { + if (j == 0) + { + hname = "-"; + break; + } + + hname = General.Map.TextureHashName[j]; + break; + } + } + + for (int j = 0; j < General.Map.TextureHashKey.Count; j++) + { + if (tlow == General.Map.TextureHashKey[j]) + { + if (j == 0) + { + lname = "-"; + break; + } + + lname = General.Map.TextureHashName[j]; + break; + } + } + + for (int j = 0; j < General.Map.TextureHashKey.Count; j++) + { + if (tmid == General.Map.TextureHashKey[j]) + { + if (j == 0) + { + mname = "-"; + break; + } + + mname = General.Map.TextureHashName[j]; + break; + } + }*/ + + s = map.CreateSidedef(l, false, sectorlink[sc]); + + s.HashTexHigh = thigh; + s.HashTexMid = tmid; + s.HashTexLow = tlow; + + s.Update(offsetx, offsety, hname, mname, lname); + //General.Map.Config.D64TextureIndex[thigh].Title, + //General.Map.Config.D64TextureIndex[tmid].Title, + //General.Map.Config.D64TextureIndex[tlow].Title); + } + else + { + General.ErrorLogger.Add(ErrorType.Warning, "Sidedef " + s2 + " references invalid sector " + sc + ". Sidedef has been removed."); + } + } + else + { + General.ErrorLogger.Add(ErrorType.Warning, "Linedef " + i + " references invalid sidedef " + s2 + ". Sidedef has been removed."); + } + } + } + else + { + General.ErrorLogger.Add(ErrorType.Warning, "Linedef " + i + " is zero-length. Linedef has been removed."); + } + } + else + { + General.ErrorLogger.Add(ErrorType.Warning, "Linedef " + i + " references one or more invalid vertices. Linedef has been removed."); + } + } + + // Done + linedefsmem.Dispose(); + sidedefsmem.Dispose(); + } + + // This reads the MACROS from WAD file + /*private void ReadMacros(MapSet map, int firstindex) + { + MemoryStream mem; + BinaryReader reader; + int count; + int setcount; + int specials; + int type, tag, batch; + int i = 0; + + // Get the lump from wad file + Lump lump = wad.FindLump("MACROS", firstindex); + if (lump == null) throw new Exception("Could not find required lump MACROS!"); + + if (lump.Length <= 4) + return; + + // Prepare to read the items + mem = new MemoryStream(lump.Stream.ReadAllBytes()); + reader = new BinaryReader(mem); + + count = reader.ReadUInt16(); + specials = reader.ReadUInt16(); + + map.NumMacros = count; + + if (count > 0) + { + // Read items from the lump + for(i = 0; i < count; i++) + { + setcount = reader.ReadUInt16(); + map.Macros[i] = new Macro(setcount); + for (int j = 0; j < setcount + 1; j++) + { + batch = reader.ReadInt16(); + tag = reader.ReadInt16(); + type = reader.ReadInt16(); + + if(j < setcount) // avoid reading the 'dummy' macro + map.Macros[i].Set(j, type, batch, tag); + } + } + } + + // Done + mem.Dispose(); + }*/ + + #endregion + + #region ================== Writing + + // This writes a MapSet to the file + public override void Write(MapSet map, string mapname, int position) + { + Dictionary vertexids = new Dictionary(); + Dictionary sidedefids = new Dictionary(); + Dictionary sectorids = new Dictionary(); + + //WriteMapInfo(map, mapname); // DON'T USE + + // First index everything + foreach (Vertex v in map.Vertices) vertexids.Add(v, vertexids.Count); + foreach (Sidedef sd in map.Sidedefs) sidedefids.Add(sd, sidedefids.Count); + foreach (Sector s in map.Sectors) sectorids.Add(s, sectorids.Count); + + // Write lumps to wad (note the backwards order because they + // are all inserted at position+1 when not found) + //WriteMacros(map, position, manager.Config.MapLumpNames); + WriteLights(map, position, manager.Config.MapLumpNames); + WriteSectors(map, position, manager.Config.MapLumpNames); + WriteVertices(map, position, manager.Config.MapLumpNames); + WriteSidedefs(map, position, manager.Config.MapLumpNames, sectorids); + WriteLinedefs(map, position, manager.Config.MapLumpNames, sidedefids, vertexids); + WriteThings(map, position, manager.Config.MapLumpNames); + } + + // This writes the map information to the map header lump + /*private void WriteMapInfo(MapSet map, string mapname) + { + MemoryStream mem; + BinaryWriter writer; + Lump lump; + string name = General.Map.Map.MapInfo.MapName; + int musid = General.Map.Map.MapInfo.MusicID; + PixelColor fogcolor = General.Map.Map.MapInfo.FogColor; + PixelColor topcolor = General.Map.Map.MapInfo.TopSkyColor; + PixelColor lowercolor = General.Map.Map.MapInfo.LowerSkyColor; + string[] intertext = General.Map.Map.MapInfo.IntermissionText; + bool overridesky = General.Map.Map.MapInfo.OverrideSky; + bool finale = General.Map.Map.MapInfo.Finale; + byte type = General.Map.Map.MapInfo.Type; + + mem = new MemoryStream(); + writer = new BinaryWriter(mem, WAD.ENCODING); + + General.Map.Map.MapInfo.WriteString(writer, name, 16); + + for (int i = 0; i < MapInfo.INTERMISSION_LINES; i++) + map.MapInfo.WriteString(writer, intertext[i], 28); + + writer.Write((bool)overridesky); + writer.Write((bool)finale); + writer.Write((byte)type); + writer.Write((int)fogcolor.ToInt()); + writer.Write((int)topcolor.ToInt()); + writer.Write((int)lowercolor.ToInt()); + writer.Write((Int16)musid); + + MapManager.RemoveSpecificLump(wad, mapname, 0, MapManager.TEMP_MAP_HEADER, manager.Config.MapLumpNames); + lump = wad.Insert(mapname, 0, (int)mem.Length); + lump.Stream.Seek(0, SeekOrigin.Begin); + mem.WriteTo(lump.Stream); + mem.Flush(); + }*/ + + // This writes the THINGS to WAD file + private void WriteThings(MapSet map, int position, IDictionary maplumps) + { + MemoryStream mem; + BinaryWriter writer; + Lump lump; + int insertpos; + int flags; + + // Create memory to write to + mem = new MemoryStream(); + writer = new BinaryWriter(mem, WAD.ENCODING); + + // Go for all things + foreach (Thing t in map.Things) + { + // Convert flags + flags = 0; + foreach (KeyValuePair f in t.Flags) + { + int fnum; + if (f.Value && int.TryParse(f.Key, out fnum)) flags |= fnum; + } + + // Write properties to stream + // Write properties to stream + writer.Write((Int16)t.Position.x); + writer.Write((Int16)t.Position.y); + writer.Write((Int16)t.Position.z); + writer.Write((Int16)Angle2D.RealToDoom(t.Angle)); + writer.Write((UInt16)t.Type); + writer.Write((UInt16)flags); + writer.Write((UInt16)t.Tag); + } + + // Find insert position and remove old lump + insertpos = MapManager.RemoveSpecificLump(wad, "THINGS", position, MapManager.TEMP_MAP_HEADER, maplumps); + if (insertpos == -1) insertpos = position + 1; + if (insertpos > wad.Lumps.Count) insertpos = wad.Lumps.Count; + + // Create the lump from memory + lump = wad.Insert("THINGS", insertpos, (int)mem.Length); + lump.Stream.Seek(0, SeekOrigin.Begin); + mem.WriteTo(lump.Stream); + } + + // This writes the VERTEXES to WAD file + private void WriteVertices(MapSet map, int position, IDictionary maplumps) + { + MemoryStream mem; + BinaryWriter writer; + Lump lump; + int insertpos; + + // Create memory to write to + mem = new MemoryStream(); + writer = new BinaryWriter(mem, WAD.ENCODING); + + // Go for all vertices + foreach (Vertex v in map.Vertices) + { + // Write properties to stream + writer.Write((Int32)((int)Math.Round(v.Position.x) * 65536)); + writer.Write((Int32)((int)Math.Round(v.Position.y) * 65536)); + } + + // Find insert position and remove old lump + insertpos = MapManager.RemoveSpecificLump(wad, "VERTEXES", position, MapManager.TEMP_MAP_HEADER, maplumps); + if (insertpos == -1) insertpos = position + 1; + if (insertpos > wad.Lumps.Count) insertpos = wad.Lumps.Count; + + // Create the lump from memory + lump = wad.Insert("VERTEXES", insertpos, (int)mem.Length); + lump.Stream.Seek(0, SeekOrigin.Begin); + mem.WriteTo(lump.Stream); + } + + // This writes the LINEDEFS to WAD file + private void WriteLinedefs(MapSet map, int position, IDictionary maplumps, IDictionary sidedefids, IDictionary vertexids) + { + MemoryStream mem; + BinaryWriter writer; + Lump lump; + ushort sid; + int insertpos; + uint flags; + + // Create memory to write to + mem = new MemoryStream(); + writer = new BinaryWriter(mem, WAD.ENCODING); + + // Go for all lines + foreach (Linedef l in map.Linedefs) + { + // Convert flags + flags = 0; + foreach (KeyValuePair f in l.Flags) + { + uint fnum; + if (f.Value && uint.TryParse(f.Key, out fnum)) flags |= fnum; + } + + // Write properties to stream + writer.Write((UInt16)vertexids[l.Start]); + writer.Write((UInt16)vertexids[l.End]); + writer.Write((UInt32)(flags | l.SwitchMask)); + writer.Write((UInt16)(l.Action | l.Activate)); + writer.Write((UInt16)l.Tag); + + // Front sidedef + if (l.Front == null) sid = ushort.MaxValue; + else sid = (UInt16)sidedefids[l.Front]; + writer.Write(sid); + + // Back sidedef + if (l.Back == null) sid = ushort.MaxValue; + else sid = (UInt16)sidedefids[l.Back]; + writer.Write(sid); + } + + // Find insert position and remove old lump + insertpos = MapManager.RemoveSpecificLump(wad, "LINEDEFS", position, MapManager.TEMP_MAP_HEADER, maplumps); + if (insertpos == -1) insertpos = position + 1; + if (insertpos > wad.Lumps.Count) insertpos = wad.Lumps.Count; + + // Create the lump from memory + lump = wad.Insert("LINEDEFS", insertpos, (int)mem.Length); + lump.Stream.Seek(0, SeekOrigin.Begin); + mem.WriteTo(lump.Stream); + } + + // This writes the SIDEDEFS to WAD file + private void WriteSidedefs(MapSet map, int position, IDictionary maplumps, IDictionary sectorids) + { + MemoryStream mem; + BinaryWriter writer; + Lump lump; + int low, mid, top; + int insertpos; + + // Create memory to write to + mem = new MemoryStream(); + writer = new BinaryWriter(mem, WAD.ENCODING); + + // Go for all sidedefs + foreach (Sidedef sd in map.Sidedefs) + { + string ht; + string lt; + string mt; + + // Write properties to stream + writer.Write((Int16)sd.OffsetX); + writer.Write((Int16)sd.OffsetY); + + low = 0; + mid = 0; + top = 0; + + ht = sd.HighTexture; + lt = sd.LowTexture; + mt = sd.MiddleTexture; + + if (ht == "-") + ht = "?"; + + if (lt == "-") + lt = "?"; + + if (mt == "-") + mt = "?"; + + for (int i = 0; i < General.Map.TextureHashKey.Count; i++) + { + if (ht == General.Map.TextureHashName[i]) + { + top = (int)General.Map.TextureHashKey[i]; + break; + } + } + + for (int i = 0; i < General.Map.TextureHashKey.Count; i++) + { + if (lt == General.Map.TextureHashName[i]) + { + low = (int)General.Map.TextureHashKey[i]; + break; + } + } + + for (int i = 0; i < General.Map.TextureHashKey.Count; i++) + { + if (mt == General.Map.TextureHashName[i]) + { + mid = (int)General.Map.TextureHashKey[i]; + break; + } + } + + /*foreach (TextureIndexInfo tp in General.Map.Config.D64TextureIndex) + { + if (sd.HighTexture == tp.Title) + top = tp.Index; + + if (sd.LowTexture == tp.Title) + low = tp.Index; + + if (sd.MiddleTexture == tp.Title) + mid = tp.Index; + }*/ + + writer.Write((Int16)top); + writer.Write((Int16)low); + writer.Write((Int16)mid); + writer.Write((UInt16)sectorids[sd.Sector]); + } + + // Find insert position and remove old lump + insertpos = MapManager.RemoveSpecificLump(wad, "SIDEDEFS", position, MapManager.TEMP_MAP_HEADER, maplumps); + if (insertpos == -1) insertpos = position + 1; + if (insertpos > wad.Lumps.Count) insertpos = wad.Lumps.Count; + + // Create the lump from memory + lump = wad.Insert("SIDEDEFS", insertpos, (int)mem.Length); + lump.Stream.Seek(0, SeekOrigin.Begin); + mem.WriteTo(lump.Stream); + } + + // This writes the LIGHTS to WAD file + private void WriteLights(MapSet map, int position, IDictionary maplumps) + { + MemoryStream mem; + BinaryWriter writer; + Lump lump; + int insertpos; + + // Create memory to write to + mem = new MemoryStream(); + writer = new BinaryWriter(mem, WAD.ENCODING); + + light = new List(); + + foreach (Sector s in map.Sectors) + { + AddLight(s, s.CeilColor); + AddLight(s, s.FloorColor); + AddLight(s, s.ThingColor); + AddLight(s, s.TopColor); + AddLight(s, s.LowerColor); + } + + foreach (Lights l in light) + { + // Write properties to stream + writer.Write((byte)l.color.r); + writer.Write((byte)l.color.g); + writer.Write((byte)l.color.b); + writer.Write((byte)0); + writer.Write((UInt16)l.tag); + } + + // Find insert position and remove old lump + insertpos = MapManager.RemoveSpecificLump(wad, "LIGHTS", position, MapManager.TEMP_MAP_HEADER, maplumps); + if (insertpos == -1) insertpos = position + 1; + if (insertpos > wad.Lumps.Count) insertpos = wad.Lumps.Count; + + // Create the lump from memory + lump = wad.Insert("LIGHTS", insertpos, (int)mem.Length); + lump.Stream.Seek(0, SeekOrigin.Begin); + mem.WriteTo(lump.Stream); + } + + // This writes the SECTORS to WAD file + private void WriteSectors(MapSet map, int position, IDictionary maplumps) + { + MemoryStream mem; + BinaryWriter writer; + Lump lump; + int flr, ceil; + int insertpos; + int flags; + + // Create memory to write to + mem = new MemoryStream(); + writer = new BinaryWriter(mem, WAD.ENCODING); + + // Go for all sectors + foreach (Sector s in map.Sectors) + { + string ft; + string ct; + + // Write properties to stream + writer.Write((Int16)s.FloorHeight); + writer.Write((Int16)s.CeilHeight); + + flr = 0; + ceil = 0; + + ft = s.FloorTexture; + ct = s.CeilTexture; + + if (ft == "-") + ft = "?"; + + if (ct == "-") + ct = "?"; + + for (int i = 0; i < General.Map.TextureHashKey.Count; i++) + { + if (ft == General.Map.TextureHashName[i]) + { + flr = (int)General.Map.TextureHashKey[i]; + break; + } + } + + for (int i = 0; i < General.Map.TextureHashKey.Count; i++) + { + if (ct == General.Map.TextureHashName[i]) + { + ceil = (int)General.Map.TextureHashKey[i]; + break; + } + } + + /*foreach (TextureIndexInfo tp in General.Map.Config.D64TextureIndex) + { + if (s.FloorTexture == tp.Title) + flr = tp.Index; + + if (s.CeilTexture == tp.Title) + ceil = tp.Index; + }*/ + + writer.Write((Int16)flr); + writer.Write((Int16)ceil); + + writer.Write((Int16)GetLight(s, s.FloorColor)); + writer.Write((Int16)GetLight(s, s.CeilColor)); + writer.Write((Int16)GetLight(s, s.ThingColor)); + writer.Write((Int16)GetLight(s, s.TopColor)); + writer.Write((Int16)GetLight(s, s.LowerColor)); + + writer.Write((UInt16)s.Effect); + writer.Write((UInt16)s.Tag); + + // Convert flags + flags = 0; + foreach (KeyValuePair f in s.Flags) + { + int fnum; + if (f.Value && int.TryParse(f.Key, out fnum)) flags |= fnum; + } + + writer.Write((UInt16)flags); + } + + // Find insert position and remove old lump + insertpos = MapManager.RemoveSpecificLump(wad, "SECTORS", position, MapManager.TEMP_MAP_HEADER, maplumps); + if (insertpos == -1) insertpos = position + 1; + if (insertpos > wad.Lumps.Count) insertpos = wad.Lumps.Count; + + // Create the lump from memory + lump = wad.Insert("SECTORS", insertpos, (int)mem.Length); + lump.Stream.Seek(0, SeekOrigin.Begin); + mem.WriteTo(lump.Stream); + } + + // This writes the MACROS to WAD file + /*private void WriteMacros(MapSet map, int position, IDictionary maplumps) + { + MemoryStream mem; + BinaryWriter writer; + Lump lump; + int insertpos; + int count = 0; + int specials = 0; + int i = 0; + + // Create memory to write to + mem = new MemoryStream(); + writer = new BinaryWriter(mem, WAD.ENCODING); + + // find line with highest action type + foreach (Linedef l in map.Linedefs) + { + if (l.Action >= 256) + { + if(i < l.Action) + i = l.Action; + } + } + + // set number of macros + if (i <= 0) + count = 1; + else + count = (i - 256) + 1; + + // count macro actions per batch + for (i = 0; i < count; i++) + { + // empty macro? then allocate a blank one + if (General.Map.Map.Macros[i] == null) + General.Map.Map.Macros[i] = new Macro(1); + + specials += General.Map.Map.Macros[i].Count; + } + + // write macro count and action count + writer.Write((UInt16)count); + writer.Write((UInt16)specials); + + // write actual macro data + for (i = 0; i < count; i++) + { + Macro m = General.Map.Map.Macros[i]; + + writer.Write((UInt16)m.Count); + + for (int j = 0; j < m.Count; j++) + { + writer.Write((UInt16)m.MacroData[j].batch); + writer.Write((UInt16)m.MacroData[j].tag); + writer.Write((UInt16)m.MacroData[j].type); + } + + // write 'dummy' macro padding at end + writer.Write((UInt16)0); + writer.Write((UInt16)0); + writer.Write((UInt16)0); + } + + // Find insert position and remove old lump + insertpos = MapManager.RemoveSpecificLump(wad, "MACROS", position, MapManager.TEMP_MAP_HEADER, maplumps); + if (insertpos == -1) insertpos = position + 1; + if (insertpos > wad.Lumps.Count) insertpos = wad.Lumps.Count; + + // Create the lump from memory + lump = wad.Insert("MACROS", insertpos, (int)mem.Length); + lump.Stream.Seek(0, SeekOrigin.Begin); + mem.WriteTo(lump.Stream); + }*/ + + #endregion + } +} diff --git a/Source/Core/IO/DoomColormapReader.cs b/Source/Core/IO/DoomColormapReader.cs new file mode 100644 index 0000000..ff7ad11 --- /dev/null +++ b/Source/Core/IO/DoomColormapReader.cs @@ -0,0 +1,242 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Geometry; +using System.Drawing; +using CodeImp.DoomBuilder.Data; +using CodeImp.DoomBuilder.Rendering; +using System.Drawing.Imaging; + +#endregion + +namespace CodeImp.DoomBuilder.IO +{ + internal unsafe class DoomColormapReader : IImageReader + { + #region ================== Variables + + // Palette to use + private Playpal palette; + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + public DoomColormapReader(Playpal palette) + { + // Initialize + this.palette = palette; + + // We have no destructor + GC.SuppressFinalize(this); + } + + #endregion + + #region ================== Methods + + // This validates the data as doom flat + public bool Validate(Stream stream) + { + int remainder; + + // Check if the data can be divided by 256 (each palette is 256 bytes) + remainder = (int)stream.Length % 256; + if (remainder == 0) + { + // Success when not 0 + return (stream.Length > 0); + } + + // Format invalid + return false; + } + + // This creates a Bitmap from the given data + // Returns null on failure + public Bitmap ReadAsBitmap(Stream stream, out int offsetx, out int offsety) + { + offsetx = int.MinValue; + offsety = int.MinValue; + return ReadAsBitmap(stream); + } + + // This creates a Bitmap from the given data + // Returns null on failure + public Bitmap ReadAsBitmap(Stream stream) + { + BitmapData bitmapdata; + PixelColorBlock pixeldata; + PixelColor* targetdata; + int width, height; + Bitmap bmp; + + // Read pixel data + pixeldata = ReadAsPixelData(stream, out width, out height); + if (pixeldata != null) + { + try + { + // Create bitmap and lock pixels + bmp = new Bitmap(width, height, PixelFormat.Format32bppArgb); + bitmapdata = bmp.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb); + targetdata = (PixelColor*)bitmapdata.Scan0.ToPointer(); + + // Copy the pixels + General.CopyMemory(targetdata, pixeldata.Pointer, (uint)(width * height * sizeof(PixelColor))); + + // Done + bmp.UnlockBits(bitmapdata); + } + catch (Exception e) + { + // Unable to make bitmap + General.ErrorLogger.Add(ErrorType.Error, "Unable to make Doom flat data. " + e.GetType().Name + ": " + e.Message); + return null; + } + } + else + { + // Failed loading picture + bmp = null; + } + + // Return result + return bmp; + } + + // This draws the picture to the given pixel color data + // Throws exception on failure + public unsafe void DrawToPixelData(Stream stream, PixelColor* target, int targetwidth, int targetheight, int x, int y) + { + Bitmap bmp; + BitmapData bmpdata; + PixelColor* pixels; + int ox, oy, tx, ty; + int width, height; + + // Get bitmap + bmp = ReadAsBitmap(stream); + width = bmp.Size.Width; + height = bmp.Size.Height; + + // Lock bitmap pixels + bmpdata = bmp.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb); + pixels = (PixelColor*)bmpdata.Scan0.ToPointer(); + + // Go for all pixels in the original image + for (ox = 0; ox < width; ox++) + { + for (oy = 0; oy < height; oy++) + { + // Copy this pixel? + if (pixels[oy * width + ox].a > 0.5f) + { + // Calculate target pixel and copy when within bounds + tx = x + ox; + ty = y + oy; + if ((tx >= 0) && (tx < targetwidth) && (ty >= 0) && (ty < targetheight)) + target[ty * targetwidth + tx] = pixels[oy * width + ox]; + } + } + } + + // Done + bmp.UnlockBits(bmpdata); + bmp.Dispose(); + } + + // This creates pixel color data from the given data + // Returns null on failure + private PixelColorBlock ReadAsPixelData(Stream stream, out int width, out int height) + { + BinaryReader reader = new BinaryReader(stream); + PixelColorBlock pixeldata = null; + byte[] bytes; + + // Image will be 128x128 + width = 128; + height = 128; + +#if !DEBUG + try + { +#endif + + // Allocate memory + pixeldata = new PixelColorBlock(width, height); + pixeldata.Clear(); + + // Read flat bytes from stream + bytes = new byte[width * height]; + stream.Read(bytes, 0, width * height); + + // Draw blocks using the palette + // We want to draw 8x8 blocks for each color + // 16 wide and 16 high + uint i = 0; + for (int by = 0; by < 16; by++) + { + for (int bx = 0; bx < 16; bx++) + { + PixelColor bc = palette[bytes[i++]]; + PixelColor bc1 = General.Colors.CreateBrightVariant(palette[bytes[i++]]); + PixelColor bc2 = General.Colors.CreateDarkVariant(palette[bytes[i++]]); + for (int py = 0; py < 8; py++) + { + for (int px = 0; px < 8; px++) + { + int p = ((by * 8) + py) * width + (bx * 8) + px; + + // We make the borders slightly brighter and darker + if ((py == 0) || (px == 0)) + pixeldata.Pointer[p] = bc1; + else if ((py == 7) || (px == 7)) + pixeldata.Pointer[p] = bc2; + else + pixeldata.Pointer[p] = bc; + } + } + } + } + + // Return pointer + return pixeldata; + +#if !DEBUG + } + catch (Exception) + { + // Return nothing + return null; + } +#endif + } + + #endregion + + } +} diff --git a/Source/Core/IO/DoomFlatReader.cs b/Source/Core/IO/DoomFlatReader.cs new file mode 100644 index 0000000..bb50324 --- /dev/null +++ b/Source/Core/IO/DoomFlatReader.cs @@ -0,0 +1,245 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Geometry; +using System.Drawing; +using CodeImp.DoomBuilder.Data; +using CodeImp.DoomBuilder.Rendering; +using System.Drawing.Imaging; + +#endregion + +namespace CodeImp.DoomBuilder.IO +{ + internal unsafe class DoomFlatReader : IImageReader + { + #region ================== Variables + + // Palette to use + private Playpal palette; + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + public DoomFlatReader(Playpal palette) + { + // Initialize + this.palette = palette; + + // We have no destructor + GC.SuppressFinalize(this); + } + + #endregion + + #region ================== Methods + + // This validates the data as doom flat + public bool Validate(Stream stream) + { + float sqrlength; + + // Check if the flat is square + sqrlength = (float)Math.Sqrt(stream.Length); + if (sqrlength == (float)Math.Truncate(sqrlength)) + { + // Success when not 0 + return ((int)sqrlength > 0); + } + // Check if the data is more than 4096 + else if (stream.Length > 4096) + { + // Success + return true; + } + + // Format invalid + return false; + } + + // This creates a Bitmap from the given data + // Returns null on failure + public Bitmap ReadAsBitmap(Stream stream, out int offsetx, out int offsety) + { + offsetx = int.MinValue; + offsety = int.MinValue; + return ReadAsBitmap(stream); + } + + // This creates a Bitmap from the given data + // Returns null on failure + public Bitmap ReadAsBitmap(Stream stream) + { + BitmapData bitmapdata; + PixelColorBlock pixeldata; + PixelColor* targetdata; + int width, height; + Bitmap bmp; + + // Read pixel data + pixeldata = ReadAsPixelData(stream, out width, out height); + if (pixeldata != null) + { + try + { + // Create bitmap and lock pixels + bmp = new Bitmap(width, height, PixelFormat.Format32bppArgb); + bitmapdata = bmp.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb); + targetdata = (PixelColor*)bitmapdata.Scan0.ToPointer(); + + // Copy the pixels + General.CopyMemory(targetdata, pixeldata.Pointer, (uint)(width * height * sizeof(PixelColor))); + + // Done + bmp.UnlockBits(bitmapdata); + } + catch (Exception e) + { + // Unable to make bitmap + General.ErrorLogger.Add(ErrorType.Error, "Unable to make Doom flat data. " + e.GetType().Name + ": " + e.Message); + return null; + } + } + else + { + // Failed loading picture + bmp = null; + } + + // Return result + return bmp; + } + + // This draws the picture to the given pixel color data + // Throws exception on failure + public unsafe void DrawToPixelData(Stream stream, PixelColor* target, int targetwidth, int targetheight, int x, int y) + { + Bitmap bmp; + BitmapData bmpdata; + PixelColor* pixels; + int ox, oy, tx, ty; + int width, height; + + // Get bitmap + bmp = ReadAsBitmap(stream); + width = bmp.Size.Width; + height = bmp.Size.Height; + + // Lock bitmap pixels + bmpdata = bmp.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb); + pixels = (PixelColor*)bmpdata.Scan0.ToPointer(); + + // Go for all pixels in the original image + for (ox = 0; ox < width; ox++) + { + for (oy = 0; oy < height; oy++) + { + // Copy this pixel? + if (pixels[oy * width + ox].a > 0.5f) + { + // Calculate target pixel and copy when within bounds + tx = x + ox; + ty = y + oy; + if ((tx >= 0) && (tx < targetwidth) && (ty >= 0) && (ty < targetheight)) + target[ty * targetwidth + tx] = pixels[oy * width + ox]; + } + } + } + + // Done + bmp.UnlockBits(bmpdata); + bmp.Dispose(); + } + + // This creates pixel color data from the given data + // Returns null on failure + private PixelColorBlock ReadAsPixelData(Stream stream, out int width, out int height) + { + BinaryReader reader = new BinaryReader(stream); + PixelColorBlock pixeldata = null; + float sqrlength; + byte[] bytes; + + // Check if the flat is square + sqrlength = (float)Math.Sqrt(stream.Length); + if (sqrlength == (float)Math.Truncate(sqrlength)) + { + // Calculate image size + width = (int)sqrlength; + height = (int)sqrlength; + } + // Check if the data is more than 4096 + else if (stream.Length > 4096) + { + // Image will be 64x64 + width = 64; + height = 64; + } + else + { + // Invalid + width = 0; + height = 0; + return null; + } + +#if !DEBUG + try + { +#endif + + // Valid width and height? + if ((width <= 0) || (height <= 0)) return null; + + // Allocate memory + pixeldata = new PixelColorBlock(width, height); + pixeldata.Clear(); + + // Read flat bytes from stream + bytes = new byte[width * height]; + stream.Read(bytes, 0, width * height); + + // Convert bytes with palette + for (uint i = 0; i < width * height; i++) pixeldata.Pointer[i] = palette[bytes[i]]; + + // Return pointer + return pixeldata; + +#if !DEBUG + } + catch (Exception) + { + // Return nothing + return null; + } +#endif + } + + #endregion + + } +} diff --git a/Source/Core/IO/DoomMapSetIO.cs b/Source/Core/IO/DoomMapSetIO.cs new file mode 100644 index 0000000..18deb4c --- /dev/null +++ b/Source/Core/IO/DoomMapSetIO.cs @@ -0,0 +1,632 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Geometry; + +#endregion + +namespace CodeImp.DoomBuilder.IO +{ + internal class DoomMapSetIO : MapSetIO + { + #region ================== Constants + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + public DoomMapSetIO(WAD wad, MapManager manager) : base(wad, manager) + { + } + + #endregion + + #region ================== Properties + + public override int MaxSidedefs { get { return ushort.MaxValue; } } + public override int MaxVertices { get { return ushort.MaxValue; } } + public override int MaxLinedefs { get { return ushort.MaxValue; } } + public override int MaxSectors { get { return ushort.MaxValue; } } + public override int MaxThings { get { return int.MaxValue; } } + public override int MinTextureOffset { get { return short.MinValue; } } + public override int MaxTextureOffset { get { return short.MaxValue; } } + public override int VertexDecimals { get { return 0; } } + public override string DecimalsFormat { get { return "0"; } } + public override bool HasLinedefTag { get { return true; } } + public override bool HasThingTag { get { return true; } } + public override bool HasThingAction { get { return false; } } + public override bool HasCustomFields { get { return false; } } + public override bool HasThingHeight { get { return false; } } + public override bool HasActionArgs { get { return false; } } + public override bool HasMixedActivations { get { return false; } } + public override bool HasPresetActivations { get { return false; } } + public override bool HasBuiltInActivations { get { return true; } } + public override bool HasNumericLinedefFlags { get { return true; } } + public override bool HasNumericThingFlags { get { return true; } } + public override bool HasNumericLinedefActivations { get { return true; } } + public override int MaxTag { get { return ushort.MaxValue; } } + public override int MinTag { get { return ushort.MinValue; } } + public override int MaxAction { get { return ushort.MaxValue; } } + public override int MinAction { get { return ushort.MinValue; } } + public override int MaxArgument { get { return 0; } } + public override int MinArgument { get { return 0; } } + public override int MaxEffect { get { return ushort.MaxValue; } } + public override int MinEffect { get { return ushort.MinValue; } } + public override int MaxBrightness { get { return short.MaxValue; } } + public override int MinBrightness { get { return short.MinValue; } } + public override int MaxThingType { get { return ushort.MaxValue; } } + public override int MinThingType { get { return ushort.MinValue; } } + public override double MaxCoordinate { get { return (double)short.MaxValue; } } + public override double MinCoordinate { get { return (double)short.MinValue; } } + public override bool InDoom64Mode { get { return false; } } // villsa + + + #endregion + + #region ================== Reading + + // This reads a map from the file and returns a MapSet + public override MapSet Read(MapSet map, string mapname) + { + Dictionary vertexlink; + Dictionary sectorlink; + + // Find the index where first map lump begins + int firstindex = wad.FindLumpIndex(mapname) + 1; + + // Read vertices + vertexlink = ReadVertices(map, firstindex); + + // Read sectors + sectorlink = ReadSectors(map, firstindex); + + // Read linedefs and sidedefs + ReadLinedefs(map, firstindex, vertexlink, sectorlink); + + // Read things + ReadThings(map, firstindex); + + // Remove unused vertices + map.RemoveUnusedVertices(); + + // Return result; + return map; + } + + // This reads the THINGS from WAD file + private void ReadThings(MapSet map, int firstindex) + { + MemoryStream mem; + BinaryReader reader; + int num, i, x, y, type, flags; + Dictionary stringflags; + float angle; + Thing t; + + // Get the lump from wad file + Lump lump = wad.FindLump("THINGS", firstindex); + if (lump == null) throw new Exception("Could not find required lump THINGS!"); + + // Prepare to read the items + mem = new MemoryStream(lump.Stream.ReadAllBytes()); + num = (int)lump.Stream.Length / 10; + reader = new BinaryReader(mem); + + // Read items from the lump + map.SetCapacity(0, 0, 0, 0, map.Things.Count + num); + for (i = 0; i < num; i++) + { + // Read properties from stream + x = reader.ReadInt16(); + y = reader.ReadInt16(); + angle = Angle2D.DoomToReal(reader.ReadInt16()); + type = reader.ReadUInt16(); + flags = reader.ReadUInt16(); + + // Make string flags + stringflags = new Dictionary(); + foreach (KeyValuePair f in manager.Config.ThingFlags) + { + int fnum; + if (int.TryParse(f.Key, out fnum)) stringflags[f.Key] = ((flags & fnum) == fnum); + } + + // Create new item + t = map.CreateThing(); + t.Update(type, x, y, 0, angle, stringflags, 0, 0, new int[Thing.NUM_ARGS]); + } + + // Done + mem.Dispose(); + } + + // This reads the VERTICES from WAD file + // Returns a lookup table with indices + private Dictionary ReadVertices(MapSet map, int firstindex) + { + MemoryStream mem; + Dictionary link; + BinaryReader reader; + int num, i, x, y; + Vertex v; + + // Get the lump from wad file + Lump lump = wad.FindLump("VERTEXES", firstindex); + if (lump == null) throw new Exception("Could not find required lump VERTEXES!"); + + // Prepare to read the items + mem = new MemoryStream(lump.Stream.ReadAllBytes()); + num = (int)lump.Stream.Length / 4; + reader = new BinaryReader(mem); + + // Create lookup table + link = new Dictionary(num); + + // Read items from the lump + map.SetCapacity(map.Vertices.Count + num, 0, 0, 0, 0); + for (i = 0; i < num; i++) + { + // Read properties from stream + x = reader.ReadInt16(); + y = reader.ReadInt16(); + + // Create new item + v = map.CreateVertex(new Vector2D((float)x, (float)y)); + + // Add it to the lookup table + link.Add(i, v); + } + + // Done + mem.Dispose(); + + // Return lookup table + return link; + } + + // This reads the SECTORS from WAD file + // Returns a lookup table with indices + private Dictionary ReadSectors(MapSet map, int firstindex) + { + MemoryStream mem; + Dictionary link; + BinaryReader reader; + int num, i, hfloor, hceil, bright, special, tag; + string tfloor, tceil; + Sector s; + + // Get the lump from wad file + Lump lump = wad.FindLump("SECTORS", firstindex); + if (lump == null) throw new Exception("Could not find required lump SECTORS!"); + + // Prepare to read the items + mem = new MemoryStream(lump.Stream.ReadAllBytes()); + num = (int)lump.Stream.Length / 26; + reader = new BinaryReader(mem); + + // Create lookup table + link = new Dictionary(num); + + // Read items from the lump + map.SetCapacity(0, 0, 0, map.Sectors.Count + num, 0); + for (i = 0; i < num; i++) + { + // Read properties from stream + hfloor = reader.ReadInt16(); + hceil = reader.ReadInt16(); + tfloor = Lump.MakeNormalName(reader.ReadBytes(8), WAD.ENCODING); + tceil = Lump.MakeNormalName(reader.ReadBytes(8), WAD.ENCODING); + bright = reader.ReadInt16(); + special = reader.ReadUInt16(); + tag = reader.ReadUInt16(); + + // Create new item + s = map.CreateSector(); + s.Update(hfloor, hceil, tfloor, tceil, special, tag, bright); + + // Add it to the lookup table + link.Add(i, s); + } + + // Done + mem.Dispose(); + + // Return lookup table + return link; + } + + // This reads the LINEDEFS and SIDEDEFS from WAD file + private void ReadLinedefs(MapSet map, int firstindex, + Dictionary vertexlink, Dictionary sectorlink) + { + MemoryStream linedefsmem, sidedefsmem; + BinaryReader readline, readside; + Lump linedefslump, sidedefslump; + int num, numsides, i, offsetx, offsety, v1, v2; + int s1, s2, action, tag, sc; + uint flags; + Dictionary stringflags; + string thigh, tmid, tlow; + Linedef l; + Sidedef s; + + // Get the linedefs lump from wad file + linedefslump = wad.FindLump("LINEDEFS", firstindex); + if (linedefslump == null) throw new Exception("Could not find required lump LINEDEFS!"); + + // Get the sidedefs lump from wad file + sidedefslump = wad.FindLump("SIDEDEFS", firstindex); + if (sidedefslump == null) throw new Exception("Could not find required lump SIDEDEFS!"); + + // Prepare to read the items + linedefsmem = new MemoryStream(linedefslump.Stream.ReadAllBytes()); + sidedefsmem = new MemoryStream(sidedefslump.Stream.ReadAllBytes()); + num = (int)linedefslump.Stream.Length / 14; + numsides = (int)sidedefslump.Stream.Length / 30; + readline = new BinaryReader(linedefsmem); + readside = new BinaryReader(sidedefsmem); + + // Read items from the lump + map.SetCapacity(0, map.Linedefs.Count + num, map.Sidedefs.Count + numsides, 0, 0); + for (i = 0; i < num; i++) + { + // Read properties from stream + v1 = readline.ReadUInt16(); + v2 = readline.ReadUInt16(); + flags = readline.ReadUInt32(); + action = readline.ReadUInt16(); + tag = readline.ReadUInt16(); + s1 = readline.ReadUInt16(); + s2 = readline.ReadUInt16(); + + // Make string flags + stringflags = new Dictionary(); + foreach (string f in manager.Config.SortedLinedefFlags) + { + uint fnum; + if (uint.TryParse(f, out fnum)) stringflags[f] = ((flags & fnum) == fnum); + } + + // Create new linedef + if (vertexlink.ContainsKey(v1) && vertexlink.ContainsKey(v2)) + { + // Check if not zero-length + if (Vector2D.ManhattanDistance(vertexlink[v1].Position, vertexlink[v2].Position) > 0.0001f) + { + l = map.CreateLinedef(vertexlink[v1], vertexlink[v2]); + l.Update(stringflags, 0, tag, action, 0, new int[Linedef.NUM_ARGS]); + l.UpdateCache(); + + // Line has a front side? + if (s1 != ushort.MaxValue) + { + // Read front sidedef + if ((s1 * 30L) <= (sidedefsmem.Length - 30L)) + { + sidedefsmem.Seek(s1 * 30, SeekOrigin.Begin); + offsetx = readside.ReadInt16(); + offsety = readside.ReadInt16(); + thigh = Lump.MakeNormalName(readside.ReadBytes(8), WAD.ENCODING); + tlow = Lump.MakeNormalName(readside.ReadBytes(8), WAD.ENCODING); + tmid = Lump.MakeNormalName(readside.ReadBytes(8), WAD.ENCODING); + sc = readside.ReadUInt16(); + + // Create front sidedef + if (sectorlink.ContainsKey(sc)) + { + s = map.CreateSidedef(l, true, sectorlink[sc]); + s.Update(offsetx, offsety, thigh, tmid, tlow); + } + else + { + General.ErrorLogger.Add(ErrorType.Warning, "Sidedef " + s1 + " references invalid sector " + sc + ". Sidedef has been removed."); + } + } + else + { + General.ErrorLogger.Add(ErrorType.Warning, "Linedef references invalid sidedef " + s1 + ". Sidedef has been removed."); + } + } + + // Line has a back side? + if (s2 != ushort.MaxValue) + { + // Read back sidedef + if ((s2 * 30L) <= (sidedefsmem.Length - 30L)) + { + sidedefsmem.Seek(s2 * 30, SeekOrigin.Begin); + offsetx = readside.ReadInt16(); + offsety = readside.ReadInt16(); + thigh = Lump.MakeNormalName(readside.ReadBytes(8), WAD.ENCODING); + tlow = Lump.MakeNormalName(readside.ReadBytes(8), WAD.ENCODING); + tmid = Lump.MakeNormalName(readside.ReadBytes(8), WAD.ENCODING); + sc = readside.ReadUInt16(); + + // Create back sidedef + if (sectorlink.ContainsKey(sc)) + { + s = map.CreateSidedef(l, false, sectorlink[sc]); + s.Update(offsetx, offsety, thigh, tmid, tlow); + } + else + { + General.ErrorLogger.Add(ErrorType.Warning, "Sidedef " + s2 + " references invalid sector " + sc + ". Sidedef has been removed."); + } + } + else + { + General.ErrorLogger.Add(ErrorType.Warning, "Linedef " + i + " references invalid sidedef " + s2 + ". Sidedef has been removed."); + } + } + } + else + { + General.ErrorLogger.Add(ErrorType.Warning, "Linedef " + i + " is zero-length. Linedef has been removed."); + } + } + else + { + General.ErrorLogger.Add(ErrorType.Warning, "Linedef " + i + " references one or more invalid vertices. Linedef has been removed."); + } + } + + // Done + linedefsmem.Dispose(); + sidedefsmem.Dispose(); + } + + #endregion + + #region ================== Writing + + // This writes a MapSet to the file + public override void Write(MapSet map, string mapname, int position) + { + Dictionary vertexids = new Dictionary(); + Dictionary sidedefids = new Dictionary(); + Dictionary sectorids = new Dictionary(); + + // First index everything + foreach (Vertex v in map.Vertices) vertexids.Add(v, vertexids.Count); + foreach (Sidedef sd in map.Sidedefs) sidedefids.Add(sd, sidedefids.Count); + foreach (Sector s in map.Sectors) sectorids.Add(s, sectorids.Count); + + // Write lumps to wad (note the backwards order because they + // are all inserted at position+1 when not found) + WriteSectors(map, position, manager.Config.MapLumpNames); + WriteVertices(map, position, manager.Config.MapLumpNames); + WriteSidedefs(map, position, manager.Config.MapLumpNames, sectorids); + WriteLinedefs(map, position, manager.Config.MapLumpNames, sidedefids, vertexids); + WriteThings(map, position, manager.Config.MapLumpNames); + } + + // This writes the THINGS to WAD file + private void WriteThings(MapSet map, int position, IDictionary maplumps) + { + MemoryStream mem; + BinaryWriter writer; + Lump lump; + int insertpos; + int flags; + + // Create memory to write to + mem = new MemoryStream(); + writer = new BinaryWriter(mem, WAD.ENCODING); + + // Go for all things + foreach (Thing t in map.Things) + { + // Convert flags + flags = 0; + foreach (KeyValuePair f in t.Flags) + { + int fnum; + if (f.Value && int.TryParse(f.Key, out fnum)) flags |= fnum; + } + + // Write properties to stream + writer.Write((Int16)t.Position.x); + writer.Write((Int16)t.Position.y); + writer.Write((Int16)Angle2D.RealToDoom(t.Angle)); + writer.Write((UInt16)t.Type); + writer.Write((UInt16)flags); + } + + // Find insert position and remove old lump + insertpos = MapManager.RemoveSpecificLump(wad, "THINGS", position, MapManager.TEMP_MAP_HEADER, maplumps); + if (insertpos == -1) insertpos = position + 1; + if (insertpos > wad.Lumps.Count) insertpos = wad.Lumps.Count; + + // Create the lump from memory + lump = wad.Insert("THINGS", insertpos, (int)mem.Length); + lump.Stream.Seek(0, SeekOrigin.Begin); + mem.WriteTo(lump.Stream); + mem.Flush(); + } + + // This writes the VERTEXES to WAD file + private void WriteVertices(MapSet map, int position, IDictionary maplumps) + { + MemoryStream mem; + BinaryWriter writer; + Lump lump; + int insertpos; + + // Create memory to write to + mem = new MemoryStream(); + writer = new BinaryWriter(mem, WAD.ENCODING); + + // Go for all vertices + foreach (Vertex v in map.Vertices) + { + // Write properties to stream + writer.Write((Int16)(int)Math.Round(v.Position.x)); + writer.Write((Int16)(int)Math.Round(v.Position.y)); + } + + // Find insert position and remove old lump + insertpos = MapManager.RemoveSpecificLump(wad, "VERTEXES", position, MapManager.TEMP_MAP_HEADER, maplumps); + if (insertpos == -1) insertpos = position + 1; + if (insertpos > wad.Lumps.Count) insertpos = wad.Lumps.Count; + + // Create the lump from memory + lump = wad.Insert("VERTEXES", insertpos, (int)mem.Length); + lump.Stream.Seek(0, SeekOrigin.Begin); + mem.WriteTo(lump.Stream); + mem.Flush(); + } + + // This writes the LINEDEFS to WAD file + private void WriteLinedefs(MapSet map, int position, IDictionary maplumps, IDictionary sidedefids, IDictionary vertexids) + { + MemoryStream mem; + BinaryWriter writer; + Lump lump; + ushort sid; + int insertpos; + int flags; + + // Create memory to write to + mem = new MemoryStream(); + writer = new BinaryWriter(mem, WAD.ENCODING); + + // Go for all lines + foreach (Linedef l in map.Linedefs) + { + // Convert flags + flags = 0; + foreach (KeyValuePair f in l.Flags) + { + int fnum; + if (f.Value && int.TryParse(f.Key, out fnum)) flags |= fnum; + } + + // Write properties to stream + writer.Write((UInt16)vertexids[l.Start]); + writer.Write((UInt16)vertexids[l.End]); + writer.Write((UInt16)flags); + writer.Write((UInt16)l.Action); + writer.Write((UInt16)l.Tag); + + // Front sidedef + if (l.Front == null) sid = ushort.MaxValue; + else sid = (UInt16)sidedefids[l.Front]; + writer.Write(sid); + + // Back sidedef + if (l.Back == null) sid = ushort.MaxValue; + else sid = (UInt16)sidedefids[l.Back]; + writer.Write(sid); + } + + // Find insert position and remove old lump + insertpos = MapManager.RemoveSpecificLump(wad, "LINEDEFS", position, MapManager.TEMP_MAP_HEADER, maplumps); + if (insertpos == -1) insertpos = position + 1; + if (insertpos > wad.Lumps.Count) insertpos = wad.Lumps.Count; + + // Create the lump from memory + lump = wad.Insert("LINEDEFS", insertpos, (int)mem.Length); + lump.Stream.Seek(0, SeekOrigin.Begin); + mem.WriteTo(lump.Stream); + mem.Flush(); + } + + // This writes the SIDEDEFS to WAD file + private void WriteSidedefs(MapSet map, int position, IDictionary maplumps, IDictionary sectorids) + { + MemoryStream mem; + BinaryWriter writer; + Lump lump; + int insertpos; + + // Create memory to write to + mem = new MemoryStream(); + writer = new BinaryWriter(mem, WAD.ENCODING); + + // Go for all sidedefs + foreach (Sidedef sd in map.Sidedefs) + { + // Write properties to stream + writer.Write((Int16)sd.OffsetX); + writer.Write((Int16)sd.OffsetY); + writer.Write(Lump.MakeFixedName(sd.HighTexture, WAD.ENCODING)); + writer.Write(Lump.MakeFixedName(sd.LowTexture, WAD.ENCODING)); + writer.Write(Lump.MakeFixedName(sd.MiddleTexture, WAD.ENCODING)); + writer.Write((UInt16)sectorids[sd.Sector]); + } + + // Find insert position and remove old lump + insertpos = MapManager.RemoveSpecificLump(wad, "SIDEDEFS", position, MapManager.TEMP_MAP_HEADER, maplumps); + if (insertpos == -1) insertpos = position + 1; + if (insertpos > wad.Lumps.Count) insertpos = wad.Lumps.Count; + + // Create the lump from memory + lump = wad.Insert("SIDEDEFS", insertpos, (int)mem.Length); + lump.Stream.Seek(0, SeekOrigin.Begin); + mem.WriteTo(lump.Stream); + mem.Flush(); + } + + // This writes the SECTORS to WAD file + private void WriteSectors(MapSet map, int position, IDictionary maplumps) + { + MemoryStream mem; + BinaryWriter writer; + Lump lump; + int insertpos; + + // Create memory to write to + mem = new MemoryStream(); + writer = new BinaryWriter(mem, WAD.ENCODING); + + // Go for all sectors + foreach (Sector s in map.Sectors) + { + // Write properties to stream + writer.Write((Int16)s.FloorHeight); + writer.Write((Int16)s.CeilHeight); + writer.Write(Lump.MakeFixedName(s.FloorTexture, WAD.ENCODING)); + writer.Write(Lump.MakeFixedName(s.CeilTexture, WAD.ENCODING)); + writer.Write((Int16)s.Brightness); + writer.Write((UInt16)s.Effect); + writer.Write((UInt16)s.Tag); + } + + // Find insert position and remove old lump + insertpos = MapManager.RemoveSpecificLump(wad, "SECTORS", position, MapManager.TEMP_MAP_HEADER, maplumps); + if (insertpos == -1) insertpos = position + 1; + if (insertpos > wad.Lumps.Count) insertpos = wad.Lumps.Count; + + // Create the lump from memory + lump = wad.Insert("SECTORS", insertpos, (int)mem.Length); + lump.Stream.Seek(0, SeekOrigin.Begin); + mem.WriteTo(lump.Stream); + mem.Flush(); + } + + #endregion + } +} diff --git a/Source/Core/IO/DoomPictureReader.cs b/Source/Core/IO/DoomPictureReader.cs new file mode 100644 index 0000000..b00c640 --- /dev/null +++ b/Source/Core/IO/DoomPictureReader.cs @@ -0,0 +1,278 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Geometry; +using System.Drawing; +using CodeImp.DoomBuilder.Data; +using CodeImp.DoomBuilder.Rendering; +using System.Drawing.Imaging; + +#endregion + +namespace CodeImp.DoomBuilder.IO +{ + internal unsafe class DoomPictureReader : IImageReader + { + #region ================== Variables + + // Palette to use + private Playpal palette; + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + public DoomPictureReader(Playpal palette) + { + // Initialize + this.palette = palette; + + // We have no destructor + GC.SuppressFinalize(this); + } + + #endregion + + #region ================== Methods + + // This validates the data as doom picture + public bool Validate(Stream stream) + { + BinaryReader reader = new BinaryReader(stream); + int width, height; + int dataoffset; + int datalength; + int columnaddr; + + // Initialize + dataoffset = (int)stream.Position; + datalength = (int)stream.Length - (int)stream.Position; + + // Need at least 4 bytes + if (datalength < 4) return false; + + // Read size and offset + width = reader.ReadInt16(); + height = reader.ReadInt16(); + reader.ReadInt16(); + reader.ReadInt16(); + + // Valid width and height? + if ((width <= 0) || (height <= 0)) return false; + + // Go for all columns + for (int x = 0; x < width; x++) + { + // Get column address + columnaddr = reader.ReadInt32(); + + // Check if address is outside valid range + if ((columnaddr < (8 + width * 4)) || (columnaddr >= datalength)) return false; + } + + // Return success + return true; + } + + // This creates a Bitmap from the given data + // Returns null on failure + public Bitmap ReadAsBitmap(Stream stream) + { + int x, y; + return ReadAsBitmap(stream, out x, out y); + } + + // This creates a Bitmap from the given data + // Returns null on failure + public Bitmap ReadAsBitmap(Stream stream, out int offsetx, out int offsety) + { + BitmapData bitmapdata; + PixelColorBlock pixeldata; + PixelColor* targetdata; + int width, height; + Bitmap bmp; + + // Read pixel data + pixeldata = ReadAsPixelData(stream, out width, out height, out offsetx, out offsety); + if (pixeldata != null) + { + // Create bitmap and lock pixels + try + { + bmp = new Bitmap(width, height, PixelFormat.Format32bppArgb); + bitmapdata = bmp.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb); + targetdata = (PixelColor*)bitmapdata.Scan0.ToPointer(); + + // Copy the pixels + General.CopyMemory(targetdata, pixeldata.Pointer, (uint)(width * height * sizeof(PixelColor))); + + // Done + bmp.UnlockBits(bitmapdata); + } + catch (Exception e) + { + // Unable to make bitmap + General.ErrorLogger.Add(ErrorType.Error, "Unable to make Doom picture data. " + e.GetType().Name + ": " + e.Message); + return null; + } + } + else + { + // Failed loading picture + bmp = null; + } + + // Return result + return bmp; + } + + // This draws the picture to the given pixel color data + // Throws exception on failure + public void DrawToPixelData(Stream stream, PixelColor* target, int targetwidth, int targetheight, int x, int y) + { + PixelColorBlock pixeldata; + int width, height, ox, oy, tx, ty; + + // Read pixel data + pixeldata = ReadAsPixelData(stream, out width, out height, out ox, out oy); + if (pixeldata != null) + { + // Go for all source pixels + // We don't care about the original image offset, so reuse ox/oy + for (ox = 0; ox < width; ox++) + { + for (oy = 0; oy < height; oy++) + { + // Copy this pixel? + if (pixeldata.Pointer[oy * width + ox].a > 0.5f) + { + // Calculate target pixel and copy when within bounds + tx = x + ox; + ty = y + oy; + if ((tx >= 0) && (tx < targetwidth) && (ty >= 0) && (ty < targetheight)) + target[ty * targetwidth + tx] = pixeldata.Pointer[oy * width + ox]; + } + } + } + } + } + + // This creates pixel color data from the given data + // Returns null on failure + private PixelColorBlock ReadAsPixelData(Stream stream, out int width, out int height, out int offsetx, out int offsety) + { + BinaryReader reader = new BinaryReader(stream); + PixelColorBlock pixeldata = null; + int y, read_y, count, p; + int[] columns; + int dataoffset; + + // Initialize + width = 0; + height = 0; + offsetx = 0; + offsety = 0; + dataoffset = (int)stream.Position; + + // Need at least 4 bytes + if ((stream.Length - stream.Position) < 4) return null; + +#if !DEBUG + try + { +#endif + + // Read size and offset + width = reader.ReadInt16(); + height = reader.ReadInt16(); + offsetx = reader.ReadInt16(); + offsety = reader.ReadInt16(); + + // Valid width and height? + if ((width <= 0) || (height <= 0)) return null; + + // Read the column addresses + columns = new int[width]; + for (int x = 0; x < width; x++) columns[x] = reader.ReadInt32(); + + // Allocate memory + pixeldata = new PixelColorBlock(width, height); + pixeldata.Clear(); + + // Go for all columns + for (int x = 0; x < width; x++) + { + // Seek to column start + stream.Seek(dataoffset + columns[x], SeekOrigin.Begin); + + // Read first post start + y = reader.ReadByte(); + read_y = y; + + // Continue while not end of column reached + while (read_y < 255) + { + // Read number of pixels in post + count = reader.ReadByte(); + + // Skip unused pixel + stream.Seek(1, SeekOrigin.Current); + + // Draw post + for (int yo = 0; yo < count; yo++) + { + // Read pixel color index + p = reader.ReadByte(); + + // Draw pixel + pixeldata.Pointer[(y + yo) * width + x] = palette[p]; + } + + // Skip unused pixel + stream.Seek(1, SeekOrigin.Current); + + // Read next post start + read_y = reader.ReadByte(); + if (read_y < y) y += read_y; else y = read_y; + } + } + + // Return pointer + return pixeldata; + +#if !DEBUG + } + catch (Exception) + { + // Return nothing + return null; + } +#endif + } + + #endregion + } +} diff --git a/Source/Core/IO/FileImageReader.cs b/Source/Core/IO/FileImageReader.cs new file mode 100644 index 0000000..65c7325 --- /dev/null +++ b/Source/Core/IO/FileImageReader.cs @@ -0,0 +1,125 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Geometry; +using System.Drawing; +using CodeImp.DoomBuilder.Data; +using CodeImp.DoomBuilder.Rendering; +using System.Drawing.Imaging; + +#endregion + +namespace CodeImp.DoomBuilder.IO +{ + internal unsafe class FileImageReader : IImageReader + { + #region ================== Constructor / Disposer + + // Constructor + public FileImageReader() + { + // We have no destructor + GC.SuppressFinalize(this); + } + + #endregion + + #region ================== Methods + + // This creates a Bitmap from the given data + // Returns null on failure + public Bitmap ReadAsBitmap(Stream stream, out int offsetx, out int offsety) + { + offsetx = int.MinValue; + offsety = int.MinValue; + return ReadAsBitmap(stream); + } + + // This reads the image and returns a Bitmap + public Bitmap ReadAsBitmap(Stream stream) + { + try + { + return (Bitmap)Bitmap.FromStream(stream); + } + catch (Exception e) + { + // Unable to make bitmap + General.ErrorLogger.Add(ErrorType.Error, "Unable to make file image. " + e.GetType().Name + ": " + e.Message); + return null; + } + } + + // This draws the picture to the given pixel color data + // Throws exception on failure + public unsafe void DrawToPixelData(Stream stream, PixelColor* target, int targetwidth, int targetheight, int x, int y) + { + Bitmap bmp; + BitmapData bmpdata; + PixelColor* pixels; + int ox, oy, tx, ty; + int width, height; + + // Get bitmap + bmp = ReadAsBitmap(stream); + if (bmp != null) + { + width = bmp.Size.Width; + height = bmp.Size.Height; + + // Lock bitmap pixels + bmpdata = bmp.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb); + pixels = (PixelColor*)bmpdata.Scan0.ToPointer(); + + // Go for all pixels in the original image + for (ox = 0; ox < width; ox++) + { + for (oy = 0; oy < height; oy++) + { + // Copy this pixel? + if (pixels[oy * width + ox].a > 0.5f) + { + // Calculate target pixel and copy when within bounds + tx = x + ox; + ty = y + oy; + if ((tx >= 0) && (tx < targetwidth) && (ty >= 0) && (ty < targetheight)) + target[ty * targetwidth + tx] = pixels[oy * width + ox]; + } + } + } + + // Done + bmp.UnlockBits(bmpdata); + bmp.Dispose(); + } + else + { + throw new InvalidDataException(); + } + } + + #endregion + } +} diff --git a/Source/Core/IO/HexenMapSetIO.cs b/Source/Core/IO/HexenMapSetIO.cs new file mode 100644 index 0000000..662f68e --- /dev/null +++ b/Source/Core/IO/HexenMapSetIO.cs @@ -0,0 +1,656 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Geometry; + +#endregion + +namespace CodeImp.DoomBuilder.IO +{ + internal class HexenMapSetIO : MapSetIO + { + #region ================== Constants + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + public HexenMapSetIO(WAD wad, MapManager manager) : base(wad, manager) + { + } + + #endregion + + #region ================== Properties + + public override int MaxSidedefs { get { return ushort.MaxValue; } } + public override int MaxVertices { get { return ushort.MaxValue; } } + public override int MaxLinedefs { get { return ushort.MaxValue; } } + public override int MaxSectors { get { return ushort.MaxValue; } } + public override int MaxThings { get { return int.MaxValue; } } + public override int MinTextureOffset { get { return short.MinValue; } } + public override int MaxTextureOffset { get { return short.MaxValue; } } + public override int VertexDecimals { get { return 0; } } + public override string DecimalsFormat { get { return "0"; } } + public override bool HasLinedefTag { get { return false; } } + public override bool HasThingTag { get { return true; } } + public override bool HasThingAction { get { return true; } } + public override bool HasCustomFields { get { return false; } } + public override bool HasThingHeight { get { return true; } } + public override bool HasActionArgs { get { return true; } } + public override bool HasMixedActivations { get { return false; } } + public override bool HasPresetActivations { get { return true; } } + public override bool HasBuiltInActivations { get { return false; } } + public override bool HasNumericLinedefFlags { get { return true; } } + public override bool HasNumericThingFlags { get { return true; } } + public override bool HasNumericLinedefActivations { get { return true; } } + public override int MaxTag { get { return ushort.MaxValue; } } + public override int MinTag { get { return ushort.MinValue; } } + public override int MaxAction { get { return byte.MaxValue; } } + public override int MinAction { get { return byte.MinValue; } } + public override int MaxArgument { get { return byte.MaxValue; } } + public override int MinArgument { get { return byte.MinValue; } } + public override int MaxEffect { get { return ushort.MaxValue; } } + public override int MinEffect { get { return ushort.MinValue; } } + public override int MaxBrightness { get { return short.MaxValue; } } + public override int MinBrightness { get { return short.MinValue; } } + public override int MaxThingType { get { return ushort.MaxValue; } } + public override int MinThingType { get { return ushort.MinValue; } } + public override double MaxCoordinate { get { return (double)short.MaxValue; } } + public override double MinCoordinate { get { return (double)short.MinValue; } } + public override bool InDoom64Mode { get { return false; } } // villsa + + #endregion + + #region ================== Reading + + // This reads a map from the file and returns a MapSet + public override MapSet Read(MapSet map, string mapname) + { + int firstindex; + Dictionary vertexlink; + Dictionary sectorlink; + + // Find the index where first map lump begins + firstindex = wad.FindLumpIndex(mapname) + 1; + + // Read vertices + vertexlink = ReadVertices(map, firstindex); + + // Read sectors + sectorlink = ReadSectors(map, firstindex); + + // Read linedefs and sidedefs + ReadLinedefs(map, firstindex, vertexlink, sectorlink); + + // Read things + ReadThings(map, firstindex); + + // Remove unused vertices + map.RemoveUnusedVertices(); + + // Return result; + return map; + } + + // This reads the THINGS from WAD file + private void ReadThings(MapSet map, int firstindex) + { + MemoryStream mem; + BinaryReader reader; + int num, i, tag, z, action, x, y, type, flags; + int[] args = new int[Thing.NUM_ARGS]; + Dictionary stringflags; + float angle; + Thing t; + + // Get the lump from wad file + Lump lump = wad.FindLump("THINGS", firstindex); + if (lump == null) throw new Exception("Could not find required lump THINGS!"); + + // Prepare to read the items + mem = new MemoryStream(lump.Stream.ReadAllBytes()); + num = (int)lump.Stream.Length / 20; + reader = new BinaryReader(mem); + + // Read items from the lump + map.SetCapacity(0, 0, 0, 0, map.Things.Count + num); + for (i = 0; i < num; i++) + { + // Read properties from stream + tag = reader.ReadUInt16(); + x = reader.ReadInt16(); + y = reader.ReadInt16(); + z = reader.ReadInt16(); + angle = Angle2D.DoomToReal(reader.ReadInt16()); + type = reader.ReadUInt16(); + flags = reader.ReadUInt16(); + action = reader.ReadByte(); + args[0] = reader.ReadByte(); + args[1] = reader.ReadByte(); + args[2] = reader.ReadByte(); + args[3] = reader.ReadByte(); + args[4] = reader.ReadByte(); + + // Make string flags + stringflags = new Dictionary(); + foreach (KeyValuePair f in manager.Config.ThingFlags) + { + int fnum; + if (int.TryParse(f.Key, out fnum)) stringflags[f.Key] = ((flags & fnum) == fnum); + } + + // Create new item + t = map.CreateThing(); + t.Update(type, x, y, z, angle, stringflags, tag, action, args); + } + + // Done + mem.Dispose(); + } + + // This reads the VERTICES from WAD file + // Returns a lookup table with indices + private Dictionary ReadVertices(MapSet map, int firstindex) + { + MemoryStream mem; + Dictionary link; + BinaryReader reader; + int num, i, x, y; + Vertex v; + + // Get the lump from wad file + Lump lump = wad.FindLump("VERTEXES", firstindex); + if (lump == null) throw new Exception("Could not find required lump VERTEXES!"); + + // Prepare to read the items + mem = new MemoryStream(lump.Stream.ReadAllBytes()); + num = (int)lump.Stream.Length / 4; + reader = new BinaryReader(mem); + + // Create lookup table + link = new Dictionary(num); + + // Read items from the lump + map.SetCapacity(map.Vertices.Count + num, 0, 0, 0, 0); + for (i = 0; i < num; i++) + { + // Read properties from stream + x = reader.ReadInt16(); + y = reader.ReadInt16(); + + // Create new item + v = map.CreateVertex(new Vector2D((float)x, (float)y)); + + // Add it to the lookup table + link.Add(i, v); + } + + // Done + mem.Dispose(); + + // Return lookup table + return link; + } + + // This reads the SECTORS from WAD file + // Returns a lookup table with indices + private Dictionary ReadSectors(MapSet map, int firstindex) + { + MemoryStream mem; + Dictionary link; + BinaryReader reader; + int num, i, hfloor, hceil, bright, special, tag; + string tfloor, tceil; + Sector s; + + // Get the lump from wad file + Lump lump = wad.FindLump("SECTORS", firstindex); + if (lump == null) throw new Exception("Could not find required lump SECTORS!"); + + // Prepare to read the items + mem = new MemoryStream(lump.Stream.ReadAllBytes()); + num = (int)lump.Stream.Length / 26; + reader = new BinaryReader(mem); + + // Create lookup table + link = new Dictionary(num); + + // Read items from the lump + map.SetCapacity(0, 0, 0, map.Sectors.Count + num, 0); + for (i = 0; i < num; i++) + { + // Read properties from stream + hfloor = reader.ReadInt16(); + hceil = reader.ReadInt16(); + tfloor = Lump.MakeNormalName(reader.ReadBytes(8), WAD.ENCODING); + tceil = Lump.MakeNormalName(reader.ReadBytes(8), WAD.ENCODING); + bright = reader.ReadInt16(); + special = reader.ReadUInt16(); + tag = reader.ReadUInt16(); + + // Create new item + s = map.CreateSector(); + s.Update(hfloor, hceil, tfloor, tceil, special, tag, bright); + + // Add it to the lookup table + link.Add(i, s); + } + + // Done + mem.Dispose(); + + // Return lookup table + return link; + } + + // This reads the LINEDEFS and SIDEDEFS from WAD file + private void ReadLinedefs(MapSet map, int firstindex, + Dictionary vertexlink, Dictionary sectorlink) + { + MemoryStream linedefsmem, sidedefsmem; + BinaryReader readline, readside; + Lump linedefslump, sidedefslump; + int num, numsides, i, offsetx, offsety, v1, v2; + int s1, s2, flags, action, sc; + int[] args = new int[Linedef.NUM_ARGS]; + Dictionary stringflags; + string thigh, tmid, tlow; + Linedef l; + Sidedef s; + + // Get the linedefs lump from wad file + linedefslump = wad.FindLump("LINEDEFS", firstindex); + if (linedefslump == null) throw new Exception("Could not find required lump LINEDEFS!"); + + // Get the sidedefs lump from wad file + sidedefslump = wad.FindLump("SIDEDEFS", firstindex); + if (sidedefslump == null) throw new Exception("Could not find required lump SIDEDEFS!"); + + // Prepare to read the items + linedefsmem = new MemoryStream(linedefslump.Stream.ReadAllBytes()); + sidedefsmem = new MemoryStream(sidedefslump.Stream.ReadAllBytes()); + num = (int)linedefslump.Stream.Length / 16; + numsides = (int)sidedefslump.Stream.Length / 30; + readline = new BinaryReader(linedefsmem); + readside = new BinaryReader(sidedefsmem); + + // Read items from the lump + map.SetCapacity(0, map.Linedefs.Count + num, map.Sidedefs.Count + numsides, 0, 0); + for (i = 0; i < num; i++) + { + // Read properties from stream + v1 = readline.ReadUInt16(); + v2 = readline.ReadUInt16(); + flags = readline.ReadUInt16(); + action = readline.ReadByte(); + args[0] = readline.ReadByte(); + args[1] = readline.ReadByte(); + args[2] = readline.ReadByte(); + args[3] = readline.ReadByte(); + args[4] = readline.ReadByte(); + s1 = readline.ReadUInt16(); + s2 = readline.ReadUInt16(); + + // Make string flags + stringflags = new Dictionary(); + foreach (string f in manager.Config.SortedLinedefFlags) + { + int fnum; + if (int.TryParse(f, out fnum)) stringflags[f] = ((flags & fnum) == fnum); + } + + // Create new linedef + if (vertexlink.ContainsKey(v1) && vertexlink.ContainsKey(v2)) + { + // Check if not zero-length + if (Vector2D.ManhattanDistance(vertexlink[v1].Position, vertexlink[v2].Position) > 0.0001f) + { + l = map.CreateLinedef(vertexlink[v1], vertexlink[v2]); + l.Update(stringflags, (flags & manager.Config.LinedefActivationsFilter), 0, action, 0, args); + l.UpdateCache(); + + // Line has a front side? + if (s1 != ushort.MaxValue) + { + // Read front sidedef + sidedefsmem.Seek(s1 * 30, SeekOrigin.Begin); + if ((s1 * 30L) <= (sidedefsmem.Length - 30L)) + { + offsetx = readside.ReadInt16(); + offsety = readside.ReadInt16(); + thigh = Lump.MakeNormalName(readside.ReadBytes(8), WAD.ENCODING); + tlow = Lump.MakeNormalName(readside.ReadBytes(8), WAD.ENCODING); + tmid = Lump.MakeNormalName(readside.ReadBytes(8), WAD.ENCODING); + sc = readside.ReadUInt16(); + + // Create front sidedef + if (sectorlink.ContainsKey(sc)) + { + s = map.CreateSidedef(l, true, sectorlink[sc]); + s.Update(offsetx, offsety, thigh, tmid, tlow); + } + else + { + General.ErrorLogger.Add(ErrorType.Warning, "Sidedef " + s1 + " references invalid sector " + sc + ". Sidedef has been removed."); + } + } + else + { + General.ErrorLogger.Add(ErrorType.Warning, "Linedef " + i + " references invalid sidedef " + s1 + ". Sidedef has been removed."); + } + } + + // Line has a back side? + if (s2 != ushort.MaxValue) + { + // Read back sidedef + sidedefsmem.Seek(s2 * 30, SeekOrigin.Begin); + if ((s2 * 30L) <= (sidedefsmem.Length - 30L)) + { + offsetx = readside.ReadInt16(); + offsety = readside.ReadInt16(); + thigh = Lump.MakeNormalName(readside.ReadBytes(8), WAD.ENCODING); + tlow = Lump.MakeNormalName(readside.ReadBytes(8), WAD.ENCODING); + tmid = Lump.MakeNormalName(readside.ReadBytes(8), WAD.ENCODING); + sc = readside.ReadUInt16(); + + // Create back sidedef + if (sectorlink.ContainsKey(sc)) + { + s = map.CreateSidedef(l, false, sectorlink[sc]); + s.Update(offsetx, offsety, thigh, tmid, tlow); + } + else + { + General.ErrorLogger.Add(ErrorType.Warning, "Sidedef " + s2 + " references invalid sector " + sc + ". Sidedef has been removed."); + } + } + else + { + General.ErrorLogger.Add(ErrorType.Warning, "Linedef " + i + " references invalid sidedef " + s2 + ". Sidedef has been removed."); + } + } + } + else + { + General.ErrorLogger.Add(ErrorType.Warning, "Linedef " + i + " is zero-length. Linedef has been removed."); + } + } + else + { + General.ErrorLogger.Add(ErrorType.Warning, "Linedef " + i + " references one or more invalid vertices. Linedef has been removed."); + } + } + + // Done + linedefsmem.Dispose(); + sidedefsmem.Dispose(); + } + + #endregion + + #region ================== Writing + + // This writes a MapSet to the file + public override void Write(MapSet map, string mapname, int position) + { + Dictionary vertexids = new Dictionary(); + Dictionary sidedefids = new Dictionary(); + Dictionary sectorids = new Dictionary(); + + // First index everything + foreach (Vertex v in map.Vertices) vertexids.Add(v, vertexids.Count); + foreach (Sidedef sd in map.Sidedefs) sidedefids.Add(sd, sidedefids.Count); + foreach (Sector s in map.Sectors) sectorids.Add(s, sectorids.Count); + + // Write lumps to wad (note the backwards order because they + // are all inserted at position+1 when not found) + WriteSectors(map, position, manager.Config.MapLumpNames); + WriteVertices(map, position, manager.Config.MapLumpNames); + WriteSidedefs(map, position, manager.Config.MapLumpNames, sectorids); + WriteLinedefs(map, position, manager.Config.MapLumpNames, sidedefids, vertexids); + WriteThings(map, position, manager.Config.MapLumpNames); + } + + // This writes the THINGS to WAD file + private void WriteThings(MapSet map, int position, IDictionary maplumps) + { + MemoryStream mem; + BinaryWriter writer; + Lump lump; + int insertpos; + int flags; + + // Create memory to write to + mem = new MemoryStream(); + writer = new BinaryWriter(mem, WAD.ENCODING); + + // Go for all things + foreach (Thing t in map.Things) + { + // Convert flags + flags = 0; + foreach (KeyValuePair f in t.Flags) + { + int fnum; + if (f.Value && int.TryParse(f.Key, out fnum)) flags |= fnum; + } + + // Write properties to stream + // Write properties to stream + writer.Write((UInt16)t.Tag); + writer.Write((Int16)t.Position.x); + writer.Write((Int16)t.Position.y); + writer.Write((Int16)t.Position.z); + writer.Write((Int16)Angle2D.RealToDoom(t.Angle)); + writer.Write((UInt16)t.Type); + writer.Write((UInt16)flags); + writer.Write((Byte)t.Action); + writer.Write((Byte)t.Args[0]); + writer.Write((Byte)t.Args[1]); + writer.Write((Byte)t.Args[2]); + writer.Write((Byte)t.Args[3]); + writer.Write((Byte)t.Args[4]); + } + + // Find insert position and remove old lump + insertpos = MapManager.RemoveSpecificLump(wad, "THINGS", position, MapManager.TEMP_MAP_HEADER, maplumps); + if (insertpos == -1) insertpos = position + 1; + if (insertpos > wad.Lumps.Count) insertpos = wad.Lumps.Count; + + // Create the lump from memory + lump = wad.Insert("THINGS", insertpos, (int)mem.Length); + lump.Stream.Seek(0, SeekOrigin.Begin); + mem.WriteTo(lump.Stream); + } + + // This writes the VERTEXES to WAD file + private void WriteVertices(MapSet map, int position, IDictionary maplumps) + { + MemoryStream mem; + BinaryWriter writer; + Lump lump; + int insertpos; + + // Create memory to write to + mem = new MemoryStream(); + writer = new BinaryWriter(mem, WAD.ENCODING); + + // Go for all vertices + foreach (Vertex v in map.Vertices) + { + // Write properties to stream + writer.Write((Int16)(int)Math.Round(v.Position.x)); + writer.Write((Int16)(int)Math.Round(v.Position.y)); + } + + // Find insert position and remove old lump + insertpos = MapManager.RemoveSpecificLump(wad, "VERTEXES", position, MapManager.TEMP_MAP_HEADER, maplumps); + if (insertpos == -1) insertpos = position + 1; + if (insertpos > wad.Lumps.Count) insertpos = wad.Lumps.Count; + + // Create the lump from memory + lump = wad.Insert("VERTEXES", insertpos, (int)mem.Length); + lump.Stream.Seek(0, SeekOrigin.Begin); + mem.WriteTo(lump.Stream); + } + + // This writes the LINEDEFS to WAD file + private void WriteLinedefs(MapSet map, int position, IDictionary maplumps, IDictionary sidedefids, IDictionary vertexids) + { + MemoryStream mem; + BinaryWriter writer; + Lump lump; + ushort sid; + int insertpos; + int flags; + + // Create memory to write to + mem = new MemoryStream(); + writer = new BinaryWriter(mem, WAD.ENCODING); + + // Go for all lines + foreach (Linedef l in map.Linedefs) + { + // Convert flags + flags = 0; + foreach (KeyValuePair f in l.Flags) + { + int fnum; + if (f.Value && int.TryParse(f.Key, out fnum)) flags |= fnum; + } + + // Add activates to flags + flags |= (l.Activate & manager.Config.LinedefActivationsFilter); + + // Write properties to stream + writer.Write((UInt16)vertexids[l.Start]); + writer.Write((UInt16)vertexids[l.End]); + writer.Write((UInt16)flags); + writer.Write((Byte)l.Action); + writer.Write((Byte)l.Args[0]); + writer.Write((Byte)l.Args[1]); + writer.Write((Byte)l.Args[2]); + writer.Write((Byte)l.Args[3]); + writer.Write((Byte)l.Args[4]); + + // Front sidedef + if (l.Front == null) sid = ushort.MaxValue; + else sid = (UInt16)sidedefids[l.Front]; + writer.Write(sid); + + // Back sidedef + if (l.Back == null) sid = ushort.MaxValue; + else sid = (UInt16)sidedefids[l.Back]; + writer.Write(sid); + } + + // Find insert position and remove old lump + insertpos = MapManager.RemoveSpecificLump(wad, "LINEDEFS", position, MapManager.TEMP_MAP_HEADER, maplumps); + if (insertpos == -1) insertpos = position + 1; + if (insertpos > wad.Lumps.Count) insertpos = wad.Lumps.Count; + + // Create the lump from memory + lump = wad.Insert("LINEDEFS", insertpos, (int)mem.Length); + lump.Stream.Seek(0, SeekOrigin.Begin); + mem.WriteTo(lump.Stream); + } + + // This writes the SIDEDEFS to WAD file + private void WriteSidedefs(MapSet map, int position, IDictionary maplumps, IDictionary sectorids) + { + MemoryStream mem; + BinaryWriter writer; + Lump lump; + int insertpos; + + // Create memory to write to + mem = new MemoryStream(); + writer = new BinaryWriter(mem, WAD.ENCODING); + + // Go for all sidedefs + foreach (Sidedef sd in map.Sidedefs) + { + // Write properties to stream + writer.Write((Int16)sd.OffsetX); + writer.Write((Int16)sd.OffsetY); + writer.Write(Lump.MakeFixedName(sd.HighTexture, WAD.ENCODING)); + writer.Write(Lump.MakeFixedName(sd.LowTexture, WAD.ENCODING)); + writer.Write(Lump.MakeFixedName(sd.MiddleTexture, WAD.ENCODING)); + writer.Write((UInt16)sectorids[sd.Sector]); + } + + // Find insert position and remove old lump + insertpos = MapManager.RemoveSpecificLump(wad, "SIDEDEFS", position, MapManager.TEMP_MAP_HEADER, maplumps); + if (insertpos == -1) insertpos = position + 1; + if (insertpos > wad.Lumps.Count) insertpos = wad.Lumps.Count; + + // Create the lump from memory + lump = wad.Insert("SIDEDEFS", insertpos, (int)mem.Length); + lump.Stream.Seek(0, SeekOrigin.Begin); + mem.WriteTo(lump.Stream); + } + + // This writes the SECTORS to WAD file + private void WriteSectors(MapSet map, int position, IDictionary maplumps) + { + MemoryStream mem; + BinaryWriter writer; + Lump lump; + int insertpos; + + // Create memory to write to + mem = new MemoryStream(); + writer = new BinaryWriter(mem, WAD.ENCODING); + + // Go for all sectors + foreach (Sector s in map.Sectors) + { + // Write properties to stream + writer.Write((Int16)s.FloorHeight); + writer.Write((Int16)s.CeilHeight); + writer.Write(Lump.MakeFixedName(s.FloorTexture, WAD.ENCODING)); + writer.Write(Lump.MakeFixedName(s.CeilTexture, WAD.ENCODING)); + writer.Write((Int16)s.Brightness); + writer.Write((UInt16)s.Effect); + writer.Write((UInt16)s.Tag); + } + + // Find insert position and remove old lump + insertpos = MapManager.RemoveSpecificLump(wad, "SECTORS", position, MapManager.TEMP_MAP_HEADER, maplumps); + if (insertpos == -1) insertpos = position + 1; + if (insertpos > wad.Lumps.Count) insertpos = wad.Lumps.Count; + + // Create the lump from memory + lump = wad.Insert("SECTORS", insertpos, (int)mem.Length); + lump.Stream.Seek(0, SeekOrigin.Begin); + mem.WriteTo(lump.Stream); + } + + #endregion + } +} diff --git a/Source/Core/IO/IImageReader.cs b/Source/Core/IO/IImageReader.cs new file mode 100644 index 0000000..f99a7e8 --- /dev/null +++ b/Source/Core/IO/IImageReader.cs @@ -0,0 +1,43 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Geometry; +using System.Drawing; +using CodeImp.DoomBuilder.Data; +using CodeImp.DoomBuilder.Rendering; +using System.Drawing.Imaging; + +#endregion + +namespace CodeImp.DoomBuilder.IO +{ + internal unsafe interface IImageReader + { + // Methods + Bitmap ReadAsBitmap(Stream stream); + Bitmap ReadAsBitmap(Stream stream, out int offsetx, out int offsety); + void DrawToPixelData(Stream stream, PixelColor* target, int targetwidth, int targetheight, int x, int y); + } +} diff --git a/Source/Core/IO/IMapSetIO.cs b/Source/Core/IO/IMapSetIO.cs new file mode 100644 index 0000000..a4d5ddf --- /dev/null +++ b/Source/Core/IO/IMapSetIO.cs @@ -0,0 +1,72 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.IO; +using CodeImp.DoomBuilder.Map; +using System.Reflection; +using System.Diagnostics; + +#endregion + +namespace CodeImp.DoomBuilder.IO +{ + public interface IMapSetIO + { + int MaxSidedefs { get; } + int VertexDecimals { get; } + string DecimalsFormat { get; } + int MaxVertices { get; } + int MaxLinedefs { get; } + int MaxSectors { get; } + int MaxThings { get; } + int MinTextureOffset { get; } + int MaxTextureOffset { get; } + bool HasLinedefTag { get; } + bool HasThingTag { get; } + bool HasThingAction { get; } + bool HasCustomFields { get; } + bool HasThingHeight { get; } + bool HasActionArgs { get; } + bool HasMixedActivations { get; } + bool HasPresetActivations { get; } + bool HasBuiltInActivations { get; } + bool HasNumericLinedefFlags { get; } + bool HasNumericThingFlags { get; } + bool HasNumericLinedefActivations { get; } + int MaxTag { get; } + int MinTag { get; } + int MaxAction { get; } + int MinAction { get; } + int MaxArgument { get; } + int MinArgument { get; } + int MaxEffect { get; } + int MinEffect { get; } + int MaxBrightness { get; } + int MinBrightness { get; } + int MaxThingType { get; } + int MinThingType { get; } + double MaxCoordinate { get; } + double MinCoordinate { get; } + bool InDoom64Mode { get; } // villsa + } +} diff --git a/Source/Core/IO/IReadWriteStream.cs b/Source/Core/IO/IReadWriteStream.cs new file mode 100644 index 0000000..5423f43 --- /dev/null +++ b/Source/Core/IO/IReadWriteStream.cs @@ -0,0 +1,89 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using CodeImp.DoomBuilder.Geometry; +using CodeImp.DoomBuilder.Rendering; +using SlimDX.Direct3D9; +using System.Drawing; +using System.IO; +using CodeImp.DoomBuilder.Map; //villsa + +#endregion + +namespace CodeImp.DoomBuilder.IO +{ + internal interface IReadWriteStream + { + // Properties + bool IsWriting { get; } + + // Management + void Begin(); + void End(); + + // Bidirectional members + void rwInt(ref int v); + void rwByte(ref byte v); + void rwShort(ref short v); + void rwString(ref string v); + void rwLong(ref long v); + void rwUInt(ref uint v); + void rwUShort(ref ushort v); + void rwULong(ref ulong v); + void rwFloat(ref float v); + void rwVector2D(ref Vector2D v); + void rwVector3D(ref Vector3D v); + void rwBool(ref bool v); + void rwLight(ref Lights v); // villsa + + // Write-only members + void wInt(int v); + void wByte(byte v); + void wShort(short v); + void wString(string v); + void wLong(long v); + void wUInt(uint v); + void wUShort(ushort v); + void wULong(ulong v); + void wFloat(float v); + void wVector2D(Vector2D v); + void wVector3D(Vector3D v); + void wBool(bool v); + void wLight(Lights v); //villsa + + // Read-only members + void rInt(out int v); + void rByte(out byte v); + void rShort(out short v); + void rString(out string v); + void rLong(out long v); + void rUInt(out uint v); + void rUShort(out ushort v); + void rULong(out ulong v); + void rFloat(out float v); + void rVector2D(out Vector2D v); + void rVector3D(out Vector3D v); + void rBool(out bool v); + void rLight(out Lights v); //villsa + } +} diff --git a/Source/Core/IO/Lump.cs b/Source/Core/IO/Lump.cs new file mode 100644 index 0000000..8724e89 --- /dev/null +++ b/Source/Core/IO/Lump.cs @@ -0,0 +1,191 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.IO; + +#endregion + +namespace CodeImp.DoomBuilder.IO +{ + public class Lump + { + #region ================== Methods + + // Allowed characters in a map lump name + internal const string MAP_LUMP_NAME_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_"; + + #endregion + + #region ================== Variables + + // Owner + private WAD owner; + + // Data stream + private ClippedStream stream; + + // Data info + private string name; + private long longname; + private byte[] fixedname; + private int offset; + private int length; + + // Disposing + private bool isdisposed = false; + + #endregion + + #region ================== Properties + + internal WAD Owner { get { return owner; } } + internal string Name { get { return name; } } + internal long LongName { get { return longname; } } + internal byte[] FixedName { get { return fixedname; } } + internal int Offset { get { return offset; } } + internal int Length { get { return length; } } + internal ClippedStream Stream { get { return stream; } } + internal bool IsDisposed { get { return isdisposed; } } + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + internal Lump(Stream data, WAD owner, byte[] fixedname, int offset, int length) + { + // Initialize + this.stream = new ClippedStream(data, offset, length); + this.owner = owner; + this.fixedname = fixedname; + this.offset = offset; + this.length = length; + + // Make name + this.name = MakeNormalName(fixedname, WAD.ENCODING).ToUpperInvariant(); + this.fixedname = MakeFixedName(name, WAD.ENCODING); + this.longname = MakeLongName(name); + + // We have no destructor + GC.SuppressFinalize(this); + } + + // Disposer + internal void Dispose() + { + // Not already disposed? + if (!isdisposed) + { + // Clean up + stream.Dispose(); + owner = null; + + // Done + isdisposed = true; + } + } + + #endregion + + #region ================== Methods + + // This returns the long value for a 8 byte texture name + public static unsafe long MakeLongName(string name) + { + long value = 0; + byte[] namebytes = Encoding.ASCII.GetBytes(name.Trim().ToUpper()); + uint bytes = (uint)namebytes.Length; + if (bytes > 8) bytes = 8; + + fixed (void* bp = namebytes) + { + General.CopyMemory(&value, bp, bytes); + } + + return value; + } + + // This makes the normal name from fixed name + public static string MakeNormalName(byte[] fixedname, Encoding encoding) + { + int length = 0; + + // Figure out the length of the lump name + while ((length < fixedname.Length) && (fixedname[length] != 0)) length++; + + // Make normal name + return encoding.GetString(fixedname, 0, length).Trim().ToUpper(); + } + + // This makes the fixed name from normal name + public static byte[] MakeFixedName(string name, Encoding encoding) + { + // Make uppercase name and count bytes + string uppername = name.Trim().ToUpper(); + int bytes = encoding.GetByteCount(uppername); + if (bytes < 8) bytes = 8; + + // Make 8 bytes, all zeros + byte[] fixedname = new byte[bytes]; + + // Write the name in bytes + encoding.GetBytes(uppername, 0, name.Length, fixedname, 0); + + // Return result + return fixedname; + } + + // This copies lump data to another lump + internal void CopyTo(Lump lump) + { + BinaryReader reader; + + // Create a reader + reader = new BinaryReader(stream); + + // Copy bytes over + stream.Seek(0, SeekOrigin.Begin); + lump.Stream.Write(reader.ReadBytes((int)stream.Length), 0, (int)stream.Length); + } + + // String representation + public override string ToString() + { + return name; + } + + // This renames the lump + internal void Rename(string newname) + { + // Make name + this.fixedname = MakeFixedName(newname, WAD.ENCODING); + this.name = MakeNormalName(this.fixedname, WAD.ENCODING).ToUpperInvariant(); + this.longname = MakeLongName(newname); + + // Write changes + owner.WriteHeaders(); + } + + #endregion + } +} diff --git a/Source/Core/IO/MapSetIO.cs b/Source/Core/IO/MapSetIO.cs new file mode 100644 index 0000000..3262e02 --- /dev/null +++ b/Source/Core/IO/MapSetIO.cs @@ -0,0 +1,165 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.IO; +using CodeImp.DoomBuilder.Map; +using System.Reflection; +using System.Diagnostics; + +#endregion + +namespace CodeImp.DoomBuilder.IO +{ + internal abstract class MapSetIO : IMapSetIO + { + #region ================== Constants + + #endregion + + #region ================== Variables + + // WAD File + protected WAD wad; + + // Map manager + protected MapManager manager; + + #endregion + + #region ================== Properties + + public abstract int MaxSidedefs { get; } + public abstract int MaxVertices { get; } + public abstract int MaxLinedefs { get; } + public abstract int MaxSectors { get; } + public abstract int MaxThings { get; } + public abstract int MinTextureOffset { get; } + public abstract int MaxTextureOffset { get; } + public abstract int VertexDecimals { get; } + public abstract string DecimalsFormat { get; } + public abstract bool HasLinedefTag { get; } + public abstract bool HasThingTag { get; } + public abstract bool HasThingAction { get; } + public abstract bool HasCustomFields { get; } + public abstract bool HasThingHeight { get; } + public abstract bool HasActionArgs { get; } + public abstract bool HasMixedActivations { get; } + public abstract bool HasPresetActivations { get; } + public abstract bool HasBuiltInActivations { get; } + public abstract bool HasNumericLinedefFlags { get; } + public abstract bool HasNumericThingFlags { get; } + public abstract bool HasNumericLinedefActivations { get; } + public abstract int MaxTag { get; } + public abstract int MinTag { get; } + public abstract int MaxAction { get; } + public abstract int MinAction { get; } + public abstract int MaxArgument { get; } + public abstract int MinArgument { get; } + public abstract int MaxEffect { get; } + public abstract int MinEffect { get; } + public abstract int MaxBrightness { get; } + public abstract int MinBrightness { get; } + public abstract int MaxThingType { get; } + public abstract int MinThingType { get; } + public abstract double MaxCoordinate { get; } + public abstract double MinCoordinate { get; } + public abstract bool InDoom64Mode { get; } // villsa + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + internal MapSetIO(WAD wad, MapManager manager) + { + // Initialize + this.wad = wad; + this.manager = manager; + } + + #endregion + + #region ================== Static Methods + + // This returns and instance of the specified IO class + public static MapSetIO Create(string classname) + { + return Create(classname, null, null); + } + + // This returns and instance of the specified IO class + public static MapSetIO Create(string classname, WAD wadfile, MapManager manager) + { + object[] args; + MapSetIO result; + string fullname; + + try + { + // Create arguments + args = new object[2]; + args[0] = wadfile; + args[1] = manager; + + // Make the full class name + fullname = "CodeImp.DoomBuilder.IO." + classname; + + // Create IO class + result = (MapSetIO)General.ThisAssembly.CreateInstance(fullname, false, + BindingFlags.Default, null, args, CultureInfo.CurrentCulture, new object[0]); + + // Check result + if (result != null) + { + // Success + return result; + } + else + { + // No such class + throw new ArgumentException("No such map format interface found: \"" + classname + "\""); + } + } + // Catch errors + catch (TargetInvocationException e) + { + // Throw the actual exception + Debug.WriteLine(DateTime.Now.ToShortDateString() + " " + DateTime.Now.ToShortTimeString()); + Debug.WriteLine(e.InnerException.Source + " throws " + e.InnerException.GetType().Name + ":"); + Debug.WriteLine(e.InnerException.Message); + Debug.WriteLine(e.InnerException.StackTrace); + throw e.InnerException; + } + } + + #endregion + + #region ================== Methods + + // Required implementations + public abstract MapSet Read(MapSet map, string mapname); + public abstract void Write(MapSet map, string mapname, int position); + + #endregion + } +} diff --git a/Source/Core/IO/SerializerStream.cs b/Source/Core/IO/SerializerStream.cs new file mode 100644 index 0000000..731fe6d --- /dev/null +++ b/Source/Core/IO/SerializerStream.cs @@ -0,0 +1,237 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using CodeImp.DoomBuilder.Geometry; +using CodeImp.DoomBuilder.Rendering; +using SlimDX.Direct3D9; +using System.Drawing; +using System.IO; +using CodeImp.DoomBuilder.Map; // villsa + +#endregion + +namespace CodeImp.DoomBuilder.IO +{ + internal sealed class SerializerStream : IReadWriteStream + { + #region ================== Constants + + #endregion + + #region ================== Variables + + private Stream stream; + private BinaryWriter writer; + private Dictionary stringstable; + + #endregion + + #region ================== Properties + + public bool IsWriting { get { return true; } } + + #endregion + + #region ================== Constructor / Destructor + + // Constructor + public SerializerStream(Stream stream) + { + // Initialize + this.stream = stream; + this.writer = new BinaryWriter(stream); + this.stringstable = new Dictionary(); + } + + #endregion + + #region ================== Methods + + // Management + public void Begin() + { + // First 4 bytes are reserved for the offset of the strings table + const int offset = 0; + writer.Write(offset); + } + + public void End() + { + // Write the offset bytes + int offset = (int)writer.BaseStream.Length; + writer.Seek(0, SeekOrigin.Begin); + writer.Write(offset); + + // Write the strings + writer.Seek(0, SeekOrigin.End); + foreach (KeyValuePair str in stringstable) + writer.Write(str.Key); + } + + // Bidirectional + public void rwInt(ref int v) { writer.Write(v); } + + public void rwByte(ref byte v) { writer.Write(v); } + + public void rwShort(ref short v) { writer.Write(v); } + + public void rwString(ref string v) + { + ushort index; + if (stringstable.ContainsKey(v)) + index = stringstable[v]; + else + index = stringstable[v] = (ushort)stringstable.Count; + writer.Write(index); + } + + public void rwLong(ref long v) { writer.Write(v); } + + public void rwUInt(ref uint v) { writer.Write(v); } + + public void rwUShort(ref ushort v) { writer.Write(v); } + + public void rwULong(ref ulong v) { writer.Write(v); } + + public void rwFloat(ref float v) { writer.Write(v); } + + public void rwBool(ref bool v) { writer.Write(v); } + + public void rwVector2D(ref Vector2D v) + { + writer.Write(v.x); + writer.Write(v.y); + } + + public void rwVector3D(ref Vector3D v) + { + writer.Write(v.x); + writer.Write(v.y); + writer.Write(v.z); + } + + //villsa + public void rwLight(ref Lights v) + { + writer.Write(v.color.r); + writer.Write(v.color.g); + writer.Write(v.color.b); + writer.Write(v.color.a); + writer.Write(v.tag); + } + + // Write-only + public void wInt(int v) { writer.Write(v); } + + public void wByte(byte v) { writer.Write(v); } + + public void wShort(short v) { writer.Write(v); } + + public void wString(string v) + { + ushort index; + if (stringstable.ContainsKey(v)) + index = stringstable[v]; + else + index = stringstable[v] = (ushort)stringstable.Count; + writer.Write(index); + } + + public void wLong(long v) { writer.Write(v); } + + public void wUInt(uint v) { writer.Write(v); } + + public void wUShort(ushort v) { writer.Write(v); } + + public void wULong(ulong v) { writer.Write(v); } + + public void wFloat(float v) { writer.Write(v); } + + public void wBool(bool v) { writer.Write(v); } + + public void wVector2D(Vector2D v) + { + writer.Write(v.x); + writer.Write(v.y); + } + + public void wVector3D(Vector3D v) + { + writer.Write(v.x); + writer.Write(v.y); + writer.Write(v.z); + } + + //villsa + public void wLight(Lights v) + { + writer.Write(v.color.r); + writer.Write(v.color.g); + writer.Write(v.color.b); + writer.Write(v.color.a); + writer.Write(v.tag); + } + + // Read-only is not supported + public void rInt(out int v) { v = 0; General.Fail("Read-only is not supported on serialization stream. Consider passing the element by reference for bidirectional support."); } + + public void rByte(out byte v) { v = 0; General.Fail("Read-only is not supported on serialization stream. Consider passing the element by reference for bidirectional support."); } + + public void rShort(out short v) { v = 0; General.Fail("Read-only is not supported on serialization stream. Consider passing the element by reference for bidirectional support."); } + + public void rString(out string v) { v = ""; General.Fail("Read-only is not supported on serialization stream. Consider passing the element by reference for bidirectional support."); } + + public void rLong(out long v) { v = 0; General.Fail("Read-only is not supported on serialization stream. Consider passing the element by reference for bidirectional support."); } + + public void rUInt(out uint v) { v = 0; General.Fail("Read-only is not supported on serialization stream. Consider passing the element by reference for bidirectional support."); } + + public void rUShort(out ushort v) { v = 0; General.Fail("Read-only is not supported on serialization stream. Consider passing the element by reference for bidirectional support."); } + + public void rULong(out ulong v) { v = 0; General.Fail("Read-only is not supported on serialization stream. Consider passing the element by reference for bidirectional support."); } + + public void rFloat(out float v) { v = 0; General.Fail("Read-only is not supported on serialization stream. Consider passing the element by reference for bidirectional support."); } + + public void rBool(out bool v) { v = false; General.Fail("Read-only is not supported on serialization stream. Consider passing the element by reference for bidirectional support."); } + + public void rVector2D(out Vector2D v) + { + v = new Vector2D(); + General.Fail("Read-only is not supported on serialization stream. Consider passing the element by reference for bidirectional support."); + } + + public void rVector3D(out Vector3D v) + { + v = new Vector3D(); + General.Fail("Read-only is not supported on serialization stream. Consider passing the element by reference for bidirectional support."); + } + + //villsa + public void rLight(out Lights v) + { + v = new Lights(); + General.Fail("Read-only is not supported on serialization stream. Consider passing the element by reference for bidirectional support."); + } + + #endregion + } +} diff --git a/Source/Core/IO/UniversalCollection.cs b/Source/Core/IO/UniversalCollection.cs new file mode 100644 index 0000000..cb2e554 --- /dev/null +++ b/Source/Core/IO/UniversalCollection.cs @@ -0,0 +1,45 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.IO; +using System.Text; +using System.Globalization; +using System.Collections; +using System.Collections.Specialized; +using System.Collections.Generic; + +#endregion + +namespace CodeImp.DoomBuilder.IO +{ + public sealed class UniversalCollection : List + { + // Variables + private string comment; + + // Properties + public string Comment { get { return comment; } set { comment = value; } } + + // Overload + public void Add(string key, object value) + { + base.Add(new UniversalEntry(key, value)); + } + } +} diff --git a/Source/Core/IO/UniversalEntry.cs b/Source/Core/IO/UniversalEntry.cs new file mode 100644 index 0000000..ae808e6 --- /dev/null +++ b/Source/Core/IO/UniversalEntry.cs @@ -0,0 +1,73 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.IO; +using System.Text; +using System.Globalization; +using System.Collections; +using System.Collections.Specialized; + +#endregion + +namespace CodeImp.DoomBuilder.IO +{ + public sealed class UniversalEntry + { + #region ================== Constants + + #endregion + + #region ================== Variables + + private string key; + private object value; + + #endregion + + #region ================== Properties + + public string Key { get { return key; } } + public object Value { get { return value; } } + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + public UniversalEntry(string key, object value) + { + // Initialize + this.key = key; + this.value = value; + } + + #endregion + + #region ================== Methods + + // This checks if the value is of the given type + // Will throw and exception when it is not + public void ValidateType(Type t) + { + if (value.GetType() != t) throw new Exception("The value of entry '" + key + "' is of incompatible type (expected " + t.Name + ")"); + } + + #endregion + } +} diff --git a/Source/Core/IO/UniversalMapSetIO.cs b/Source/Core/IO/UniversalMapSetIO.cs new file mode 100644 index 0000000..d69ae60 --- /dev/null +++ b/Source/Core/IO/UniversalMapSetIO.cs @@ -0,0 +1,197 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Geometry; +using System.Windows.Forms; +using CodeImp.DoomBuilder.Config; +using CodeImp.DoomBuilder.Types; + +#endregion + +namespace CodeImp.DoomBuilder.IO +{ + internal class UniversalMapSetIO : MapSetIO + { + #region ================== Constants + + // Name of the UDMF configuration file + private const string UDMF_CONFIG_NAME = "UDMF.cfg"; + + #endregion + + #region ================== Variables + + private Configuration config; + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + public UniversalMapSetIO(WAD wad, MapManager manager) : base(wad, manager) + { + if ((manager != null) && (manager.Config != null)) + { + // Make configuration + config = new Configuration(); + + // Find a resource named UDMF.cfg + string[] resnames = General.ThisAssembly.GetManifestResourceNames(); + foreach (string rn in resnames) + { + // Found it? + if (rn.EndsWith(UDMF_CONFIG_NAME, StringComparison.InvariantCultureIgnoreCase)) + { + // Get a stream from the resource + Stream udmfcfg = General.ThisAssembly.GetManifestResourceStream(rn); + StreamReader udmfcfgreader = new StreamReader(udmfcfg, Encoding.ASCII); + + // Load configuration from stream + config.InputConfiguration(udmfcfgreader.ReadToEnd()); + + // Now we add the linedef flags, activations and thing flags + // to this list, so that these don't show up in the custom + // fields list either. We use true as dummy value (it has no meaning) + + // Add linedef flags + foreach (KeyValuePair flag in manager.Config.LinedefFlags) + config.WriteSetting("managedfields.linedef." + flag.Key, true); + + // Add linedef activations + foreach (LinedefActivateInfo activate in manager.Config.LinedefActivates) + config.WriteSetting("managedfields.linedef." + activate.Key, true); + + // Add thing flags + foreach (KeyValuePair flag in manager.Config.ThingFlags) + config.WriteSetting("managedfields.thing." + flag.Key, true); + + // Done + udmfcfgreader.Dispose(); + udmfcfg.Dispose(); + break; + } + } + } + } + + #endregion + + #region ================== Properties + + public override int MaxSidedefs { get { return int.MaxValue; } } + public override int MaxVertices { get { return int.MaxValue; } } + public override int MaxLinedefs { get { return int.MaxValue; } } + public override int MaxSectors { get { return int.MaxValue; } } + public override int MaxThings { get { return int.MaxValue; } } + public override int MinTextureOffset { get { return int.MinValue; } } + public override int MaxTextureOffset { get { return int.MaxValue; } } + public override int VertexDecimals { get { return 3; } } + public override string DecimalsFormat { get { return "0.000"; } } + public override bool HasLinedefTag { get { return true; } } + public override bool HasThingTag { get { return true; } } + public override bool HasThingAction { get { return true; } } + public override bool HasCustomFields { get { return true; } } + public override bool HasThingHeight { get { return true; } } + public override bool HasActionArgs { get { return true; } } + public override bool HasMixedActivations { get { return true; } } + public override bool HasPresetActivations { get { return false; } } + public override bool HasBuiltInActivations { get { return false; } } + public override bool HasNumericLinedefFlags { get { return false; } } + public override bool HasNumericThingFlags { get { return false; } } + public override bool HasNumericLinedefActivations { get { return false; } } + public override int MaxTag { get { return int.MaxValue; } } + public override int MinTag { get { return int.MinValue; } } + public override int MaxAction { get { return int.MaxValue; } } + public override int MinAction { get { return int.MinValue; } } + public override int MaxArgument { get { return int.MaxValue; } } + public override int MinArgument { get { return int.MinValue; } } + public override int MaxEffect { get { return int.MaxValue; } } + public override int MinEffect { get { return int.MinValue; } } + public override int MaxBrightness { get { return int.MaxValue; } } + public override int MinBrightness { get { return int.MinValue; } } + public override int MaxThingType { get { return int.MaxValue; } } + public override int MinThingType { get { return int.MinValue; } } + public override double MaxCoordinate { get { return (double)float.MaxValue; } } + public override double MinCoordinate { get { return (double)float.MinValue; } } + public override bool InDoom64Mode { get { return false; } } // villsa + + #endregion + + #region ================== Reading + + // This reads a map from the file and returns a MapSet + public override MapSet Read(MapSet map, string mapname) + { + UniversalStreamReader udmfreader = new UniversalStreamReader(); + + // Find the index where first map lump begins + int firstindex = wad.FindLumpIndex(mapname) + 1; + + // Get the TEXTMAP lump from wad file + Lump lump = wad.FindLump("TEXTMAP", firstindex); + if (lump == null) throw new Exception("Could not find required lump TEXTMAP!"); + + // Read the UDMF data + lump.Stream.Seek(0, SeekOrigin.Begin); + udmfreader.SetKnownCustomTypes = true; + udmfreader.Read(map, lump.Stream); + + // Return result + return map; + } + + #endregion + + #region ================== Writing + + // This writes a MapSet to the file + public override void Write(MapSet map, string mapname, int position) + { + UniversalStreamWriter udmfwriter = new UniversalStreamWriter(); + + // Write map to memory stream + MemoryStream memstream = new MemoryStream(); + memstream.Seek(0, SeekOrigin.Begin); + udmfwriter.RememberCustomTypes = true; + udmfwriter.Write(map, memstream, manager.Config.EngineName); + + // Find insert position and remove old lump + int insertpos = MapManager.RemoveSpecificLump(wad, "TEXTMAP", position, MapManager.TEMP_MAP_HEADER, manager.Config.MapLumpNames); + if (insertpos == -1) insertpos = position + 1; + if (insertpos > wad.Lumps.Count) insertpos = wad.Lumps.Count; + + // Create the lump from memory + Lump lump = wad.Insert("TEXTMAP", insertpos, (int)memstream.Length); + lump.Stream.Seek(0, SeekOrigin.Begin); + memstream.WriteTo(lump.Stream); + + // Done + memstream.Dispose(); + } + + #endregion + } +} + diff --git a/Source/Core/IO/UniversalParser.cs b/Source/Core/IO/UniversalParser.cs new file mode 100644 index 0000000..53d815e --- /dev/null +++ b/Source/Core/IO/UniversalParser.cs @@ -0,0 +1,838 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.IO; +using System.Text; +using System.Globalization; +using System.Collections; +using System.Collections.Specialized; +using System.Collections.Generic; + +#endregion + +namespace CodeImp.DoomBuilder.IO +{ + public sealed class UniversalParser + { + #region ================== Constants + + // Path seperator + public const string DEFAULT_SEPERATOR = "."; + + // Allowed characters in a key + public const string KEY_CHARACTERS = "abcdefghijklmnopqrstuvwxyz0123456789_"; + + // Parse mode constants + private const int PM_NOTHING = 0; + private const int PM_ASSIGNMENT = 1; + private const int PM_NUMBER = 2; + private const int PM_STRING = 3; + private const int PM_KEYWORD = 4; + + // Error strings + private const string ERROR_KEYMISSING = "Missing key name in assignment or scope."; + private const string ERROR_KEYCHARACTERS = "Invalid characters in key name."; + private const string ERROR_ASSIGNINVALID = "Invalid assignment. Missing a previous terminator symbol?"; + private const string ERROR_VALUEINVALID = "Invalid value in assignment. Missing a previous terminator symbol?"; + private const string ERROR_VALUETOOBIG = "Value too big."; + private const string ERROR_KEYWITHOUTVALUE = "Key has no value assigned."; + private const string ERROR_KEYWORDUNKNOWN = "Unknown keyword in assignment. Missing a previous terminator symbol?"; + + #endregion + + #region ================== Variables + + // Error result + private int cpErrorResult = 0; + private string cpErrorDescription = ""; + private int cpErrorLine = 0; + + // Configuration root + private UniversalCollection root = null; + + // Settings + private bool strictchecking = true; + + #endregion + + #region ================== Properties + + // Properties + public int ErrorResult { get { return cpErrorResult; } } + public string ErrorDescription { get { return cpErrorDescription; } } + public int ErrorLine { get { return cpErrorLine; } } + public UniversalCollection Root { get { return root; } } + public bool StrictChecking { get { return strictchecking; } set { strictchecking = value; } } + + #endregion + + #region ================== Constructor / Destructor + + // Constructor + public UniversalParser() + { + // Standard new configuration + NewConfiguration(); + + // We have no destructor + GC.SuppressFinalize(this); + } + + // Constructor to load a file immediately + public UniversalParser(string filename) + { + // Load configuration from file + LoadConfiguration(filename); + + // We have no destructor + GC.SuppressFinalize(this); + } + + #endregion + + #region ================== Private Methods + + // This returns a string added with escape characters + private string EscapedString(string str) + { + // Replace the \ with \\ first! + str = str.Replace("\\", "\\\\"); + str = str.Replace("\n", "\\n"); + str = str.Replace("\r", "\\r"); + str = str.Replace("\t", "\\t"); + str = str.Replace("\"", "\\\""); + + // Return result + return str; + } + + + // This raises an error + private void RaiseError(int line, string description) + { + // Raise error + cpErrorResult = 1; + cpErrorDescription = description; + cpErrorLine = line; + } + + + // This validates a given key and sets + // error properties if key is invalid and errorline > -1 + private bool ValidateKey(string key, int errorline) + { + bool validateresult = true; + + // Check if key is an empty string + if (key.Length == 0) + { + // ERROR: Missing key name in statement + if (errorline > -1) RaiseError(errorline, ERROR_KEYMISSING); + validateresult = false; + } + else + { + // Only when strict checking + if (strictchecking) + { + // Check if all characters are valid + foreach (char c in key) + { + if (KEY_CHARACTERS.IndexOf(c) == -1) + { + // ERROR: Invalid characters in key name + if (errorline > -1) RaiseError(errorline, ERROR_KEYCHARACTERS); + validateresult = false; + break; + } + } + } + } + + // Return result + return validateresult; + } + + + // This parses a structure in the given data starting + // from the given pos and line and updates pos and line. + private UniversalCollection InputStructure(ref string data, ref int pos, ref int line) + { + char c = '\0'; // current data character + int pm = PM_NOTHING; // current parse mode + string key = "", val = ""; // current key and value beign built + bool escape = false; // escape sequence? + bool endofstruct = false; // true as soon as this level struct ends + UniversalCollection cs = new UniversalCollection(); + + // Go through all of the data until + // the end or until the struct closes + // or when an arror occurred + while ((pos < data.Length) && (cpErrorResult == 0) && (endofstruct == false)) + { + // Get current character + c = data[pos]; + + // ================ What parse mode are we at? + if (pm == PM_NOTHING) + { + // Now check what character this is + switch (c) + { + case '{': // Begin of new struct + + // Validate key + if (ValidateKey(key.Trim(), line)) + { + // Next character + pos++; + + // Parse this struct and add it + cs.Add(new UniversalEntry(key.Trim(), InputStructure(ref data, ref pos, ref line))); + + // Check the last character + pos--; + + // Reset the key + key = ""; + } + + // Leave switch + break; + + case '}': // End of this struct + + // Stop parsing in this struct + endofstruct = true; + + // Leave the loop + break; + + case '=': // Assignment + + // Validate key + if (ValidateKey(key.Trim(), line)) + { + // Now parsing assignment + pm = PM_ASSIGNMENT; + } + + // Leave switch + break; + + case ';': // Terminator + + // Validate key + if (ValidateKey(key.Trim(), line)) + { + // Error: No value + RaiseError(line, ERROR_KEYWITHOUTVALUE); + } + + // Leave switch + break; + + case '\n': // New line + + // Count the line + line++; + + // Add this to the key as a space. + // Spaces are not allowed, but it will be trimmed + // when its the first or last character. + key += " "; + + // Leave switch + break; + + case '\\': // Possible comment + case '/': + + // Check for the line comment // + if (data.Substring(pos, 2) == "//") + { + // Find the next line + int np = data.IndexOf("\n", pos); + + // Next line found? + if (np > -1) + { + // Count the line + line++; + + // Skip everything on this line + pos = np; + } + else + { + // No end of line + // Skip everything else + pos = data.Length; + } + } + // Check for the block comment /* */ + else if (data.Substring(pos, 2) == "/*") + { + // Find the next closing block comment + int np = data.IndexOf("*/", pos); + + // Closing block comment found? + if (np > -1) + { + // Count the lines in the block comment + string blockdata = data.Substring(pos, np - pos + 2); + line += (blockdata.Split("\n".ToCharArray()).Length - 1); + + // Skip everything in this block + pos = np + 1; + } + else + { + // No end of line + // Skip everything else + pos = data.Length; + } + } + + // Leave switch + break; + + default: // Everything else + + // Add character to key + key += c.ToString(CultureInfo.InvariantCulture).ToLowerInvariant(); + + // Leave switch + break; + } + } + // ================ Parsing an assignment + else if (pm == PM_ASSIGNMENT) + { + // Check for string opening + if (c == '\"') + { + // Now parsing string + pm = PM_STRING; + } + // Check for numeric character + else if ("0123456789-.&".IndexOf(c.ToString(CultureInfo.InvariantCulture)) > -1) + { + // Now parsing number + pm = PM_NUMBER; + + // Go one byte back, because this + // byte is part of the number! + pos--; + } + // Check for new line + else if (c == '\n') + { + // Count the new line + line++; + } + // Check if assignment ends + else if (c == ';') + { + // End of assignment + pm = PM_NOTHING; + + // Remove this if it causes problems + key = ""; + val = ""; + } + // Otherwise (if not whitespace) it will be a keyword + else if ((c != ' ') && (c != '\t')) + { + // Now parsing a keyword + pm = PM_KEYWORD; + + // Go one byte back, because this + // byte is part of the keyword! + pos--; + } + } + // ================ Parsing a number + else if (pm == PM_NUMBER) + { + // Check if number ends here + if (c == ';') + { + // Hexadecimal? + if ((val.Length > 2) && val.StartsWith("0x", StringComparison.InvariantCultureIgnoreCase)) + { + int ival = 0; + long lval = 0; + + // Convert to int + try + { + // Convert to value + ival = System.Convert.ToInt32(val.Substring(2).Trim(), 16); + + // Add it to struct + cs.Add(new UniversalEntry(key.Trim(), ival)); + } + catch (System.OverflowException) + { + // Too large for Int32, try Int64 + try + { + // Convert to value + lval = System.Convert.ToInt64(val.Substring(2).Trim(), 16); + + // Add it to struct + cs.Add(new UniversalEntry(key.Trim(), lval)); + } + catch (System.OverflowException) + { + // Too large for Int64, return error + RaiseError(line, ERROR_VALUETOOBIG); + } + catch (System.FormatException) + { + // ERROR: Invalid value in assignment + RaiseError(line, ERROR_VALUEINVALID); + } + } + catch (System.FormatException) + { + // ERROR: Invalid value in assignment + RaiseError(line, ERROR_VALUEINVALID); + } + } + // Floating point? + else if (val.IndexOf(".") > -1) + { + float fval = 0; + + // Convert to float (remove the f first) + try { fval = System.Convert.ToSingle(val.Trim(), CultureInfo.InvariantCulture); } + catch (System.FormatException) + { + // ERROR: Invalid value in assignment + RaiseError(line, ERROR_VALUEINVALID); + } + + // Add it to struct + cs.Add(new UniversalEntry(key.Trim(), fval)); + } + else + { + int ival = 0; + long lval = 0; + + // Convert to int + try + { + // Convert to value + ival = System.Convert.ToInt32(val.Trim(), CultureInfo.InvariantCulture); + + // Add it to struct + cs.Add(new UniversalEntry(key.Trim(), ival)); + } + catch (System.OverflowException) + { + // Too large for Int32, try Int64 + try + { + // Convert to value + lval = System.Convert.ToInt64(val.Trim(), CultureInfo.InvariantCulture); + + // Add it to struct + cs.Add(new UniversalEntry(key.Trim(), lval)); + } + catch (System.OverflowException) + { + // Too large for Int64, return error + RaiseError(line, ERROR_VALUETOOBIG); + } + catch (System.FormatException) + { + // ERROR: Invalid value in assignment + RaiseError(line, ERROR_VALUEINVALID); + } + } + catch (System.FormatException) + { + // ERROR: Invalid value in assignment + RaiseError(line, ERROR_VALUEINVALID); + } + } + + // Reset key and value + key = ""; + val = ""; + + // End of assignment + pm = PM_NOTHING; + } + // Check for new line + else if (c == '\n') + { + // Count the new line + line++; + } + // Everything else is part of the value + else + { + val += c.ToString(CultureInfo.InvariantCulture); + } + } + // ================ Parsing a string + else if (pm == PM_STRING) + { + // Check if in an escape sequence + if (escape) + { + // What character? + switch (c) + { + case '\\': val += "\\"; break; + case 'n': val += "\n"; break; + case '\"': val += "\""; break; + case 'r': val += "\r"; break; + case 't': val += "\t"; break; + default: + + // Is it a number? + if ("0123456789".IndexOf(c.ToString(CultureInfo.InvariantCulture)) > -1) + { + int vv = 0; + char vc = '0'; + + // Convert the next 3 characters to a number + string v = data.Substring(pos, 3); + try { vv = System.Convert.ToInt32(v.Trim(), CultureInfo.InvariantCulture); } + catch (System.FormatException) + { + // ERROR: Invalid value in assignment + RaiseError(line, ERROR_VALUEINVALID); + } + + // Convert the number to a char + try { vc = System.Convert.ToChar(vv, CultureInfo.InvariantCulture); } + catch (System.FormatException) + { + // ERROR: Invalid value in assignment + RaiseError(line, ERROR_VALUEINVALID); + } + + // Add the char + val += vc.ToString(CultureInfo.InvariantCulture); + } + else + { + // Add the character as it is + val += c.ToString(CultureInfo.InvariantCulture); + } + + // Leave switch + break; + } + + // End of escape sequence + escape = false; + } + else + { + // Check for sequence start + if (c == '\\') + { + // Next character is of escape sequence + escape = true; + } + // Check if string ends + else if (c == '\"') + { + // Add string to struct + cs.Add(new UniversalEntry(key.Trim(), val)); + + // End of assignment + pm = PM_ASSIGNMENT; + + // Reset key and value + key = ""; + val = ""; + } + // Check for new line + else if (c == '\n') + { + // Count the new line + line++; + } + // Everything else is just part of string + else + { + // Add to value + val += c.ToString(CultureInfo.InvariantCulture); + } + } + } + // ================ Parsing a keyword + else if (pm == PM_KEYWORD) + { + // Check if keyword ends + if (c == ';') + { + // Add to the struct depending on the keyword + switch (val.Trim().ToLowerInvariant()) + { + case "true": + + // Add boolean true + cs.Add(new UniversalEntry(key.Trim(), true)); + break; + + case "false": + + // Add boolean false + cs.Add(new UniversalEntry(key.Trim(), false)); + break; + + default: + + // Unknown keyword + RaiseError(line, ERROR_KEYWORDUNKNOWN); + break; + } + + // End of assignment + pm = PM_NOTHING; + + // Reset key and value + key = ""; + val = ""; + } + // Check for new line + else if (c == '\n') + { + // Count the new line + line++; + } + // Everything else is just part of keyword + else + { + // Add to value + val += c.ToString(CultureInfo.InvariantCulture); + } + } + + // Next character + pos++; + } + + // Return the parsed result + return cs; + } + + + // This will create a data structure from the given object + private string OutputStructure(UniversalCollection cs, int level, string newline, bool whitespace) + { + string leveltabs = ""; + string spacing = ""; + StringBuilder db = new StringBuilder(""); + + // Check if this ConfigStruct is not empty + if (cs.Count > 0) + { + // Create whitespace + if (whitespace) + { + for (int i = 0; i < level; i++) leveltabs += "\t"; + spacing = " "; + } + + // Get enumerator + IEnumerator de = cs.GetEnumerator(); + + // Go for each item + for (int i = 0; i < cs.Count; i++) + { + // Go to next item + de.MoveNext(); + + // Check if the value if of collection type + if (de.Current.Value is UniversalCollection) + { + UniversalCollection c = (UniversalCollection)de.Current.Value; + + // Output recursive structure + if (whitespace) { db.Append(leveltabs); db.Append(newline); } + db.Append(leveltabs); db.Append(de.Current.Key); + if (!string.IsNullOrEmpty(c.Comment)) + { + if (whitespace) db.Append("\t"); + db.Append("// " + c.Comment); + } + db.Append(newline); + db.Append(leveltabs); db.Append("{"); db.Append(newline); + db.Append(OutputStructure(c, level + 1, newline, whitespace)); + db.Append(leveltabs); db.Append("}"); db.Append(newline); + if (whitespace) { db.Append(leveltabs); db.Append(newline); } + } + // Check if the value is of boolean type + else if (de.Current.Value is bool) + { + // Check value + if ((bool)de.Current.Value == true) + { + // Output the keyword "true" + db.Append(leveltabs); db.Append(de.Current.Key); db.Append(spacing); + db.Append("="); db.Append(spacing); db.Append("true;"); db.Append(newline); + } + else + { + // Output the keyword "false" + db.Append(leveltabs); db.Append(de.Current.Key); db.Append(spacing); + db.Append("="); db.Append(spacing); db.Append("false;"); db.Append(newline); + } + } + // Check if value is of float type + else if (de.Current.Value is float) + { + // Output the value as float (3 decimals) + float f = (float)de.Current.Value; + db.Append(leveltabs); db.Append(de.Current.Key); db.Append(spacing); db.Append("="); + db.Append(spacing); db.Append(f.ToString("0.000", CultureInfo.InvariantCulture)); db.Append(";"); db.Append(newline); + } + // Check if value is of other numeric type + else if (de.Current.Value.GetType().IsPrimitive) + { + // Output the value unquoted + db.Append(leveltabs); db.Append(de.Current.Key); db.Append(spacing); db.Append("="); + db.Append(spacing); db.Append(String.Format(CultureInfo.InvariantCulture, "{0}", de.Current.Value)); db.Append(";"); db.Append(newline); + } + else + { + // Output the value with quotes and escape characters + db.Append(leveltabs); db.Append(de.Current.Key); db.Append(spacing); db.Append("="); + db.Append(spacing); db.Append("\""); db.Append(EscapedString(de.Current.Value.ToString())); db.Append("\";"); db.Append(newline); + } + } + } + + // Return the structure + return db.ToString(); + } + + #endregion + + #region ================== Public Methods + + // This clears the last error + public void ClearError() + { + // Clear error + cpErrorResult = 0; + cpErrorDescription = ""; + cpErrorLine = 0; + } + + + // This creates a new configuration + public void NewConfiguration() + { + // Create new configuration + root = new UniversalCollection(); + } + + + // This will save the current configuration to the specified file + public bool SaveConfiguration(string filename) { return SaveConfiguration(filename, "\r\n", true); } + public bool SaveConfiguration(string filename, string newline) { return SaveConfiguration(filename, newline, true); } + public bool SaveConfiguration(string filename, string newline, bool whitespace) + { + // Kill the file if it exists + if (File.Exists(filename) == true) File.Delete(filename); + + // Open file stream for writing + FileStream fstream = File.OpenWrite(filename); + + // Create output structure and write to file + string data = OutputConfiguration(newline, whitespace); + byte[] baData = Encoding.ASCII.GetBytes(data); + fstream.Write(baData, 0, baData.Length); + fstream.Flush(); + fstream.Close(); + + // Return true when done, false when errors occurred + if (cpErrorResult == 0) return true; else return false; + } + + + // This will output the current configuration as a string + public string OutputConfiguration() { return OutputConfiguration("\r\n", true); } + public string OutputConfiguration(string newline) { return OutputConfiguration(newline, true); } + public string OutputConfiguration(string newline, bool whitespace) + { + // Simply return the configuration structure as string + return OutputStructure(root, 0, newline, whitespace); + } + + + // This will load a configuration from file + public bool LoadConfiguration(string filename) + { + // Check if the file is missing + if (File.Exists(filename) == false) + { + throw (new FileNotFoundException("File not found \"" + filename + "\"", filename)); + } + else + { + // Load the file contents + FileStream fstream = File.OpenRead(filename); + byte[] fbuffer = new byte[fstream.Length]; + fstream.Read(fbuffer, 0, fbuffer.Length); + fstream.Close(); + + // Convert byte array to string + string data = Encoding.ASCII.GetString(fbuffer); + + // Load the configuration from this data + return InputConfiguration(data); + } + } + + + // This will load a configuration from string + public bool InputConfiguration(string data) + { + // Remove returns and tabs because the + // parser only uses newline for new lines. + data = data.Replace("\r", ""); + data = data.Replace("\t", ""); + + // Clear errors + ClearError(); + + // Parse the data to the root structure + int pos = 0; + int line = 1; + root = InputStructure(ref data, ref pos, ref line); + + // Return true when done, false when errors occurred + if (cpErrorResult == 0) return true; else return false; + } + + #endregion + } +} diff --git a/Source/Core/IO/UniversalStreamReader.cs b/Source/Core/IO/UniversalStreamReader.cs new file mode 100644 index 0000000..4c15a3a --- /dev/null +++ b/Source/Core/IO/UniversalStreamReader.cs @@ -0,0 +1,517 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Geometry; +using System.Windows.Forms; +using CodeImp.DoomBuilder.Config; +using CodeImp.DoomBuilder.Types; + +#endregion + +namespace CodeImp.DoomBuilder.IO +{ + internal class UniversalStreamReader + { + #region ================== Constants + + // Name of the UDMF configuration file + private const string UDMF_CONFIG_NAME = "UDMF.cfg"; + + #endregion + + #region ================== Variables + + private Configuration config; + private bool setknowncustomtypes; + private bool strictchecking = true; + + #endregion + + #region ================== Properties + + public bool SetKnownCustomTypes { get { return setknowncustomtypes; } set { setknowncustomtypes = value; } } + public bool StrictChecking { get { return strictchecking; } set { strictchecking = value; } } + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + public UniversalStreamReader() + { + // Make configuration + config = new Configuration(); + + // Find a resource named UDMF.cfg + string[] resnames = General.ThisAssembly.GetManifestResourceNames(); + foreach (string rn in resnames) + { + // Found it? + if (rn.EndsWith(UDMF_CONFIG_NAME, StringComparison.InvariantCultureIgnoreCase)) + { + // Get a stream from the resource + Stream udmfcfg = General.ThisAssembly.GetManifestResourceStream(rn); + StreamReader udmfcfgreader = new StreamReader(udmfcfg, Encoding.ASCII); + + // Load configuration from stream + config.InputConfiguration(udmfcfgreader.ReadToEnd()); + + // Now we add the linedef flags, activations and thing flags + // to this list, so that these don't show up in the custom + // fields list either. We use true as dummy value (it has no meaning) + + // Add linedef flags + foreach (KeyValuePair flag in General.Map.Config.LinedefFlags) + config.WriteSetting("managedfields.linedef." + flag.Key, true); + + // Add linedef activations + foreach (LinedefActivateInfo activate in General.Map.Config.LinedefActivates) + config.WriteSetting("managedfields.linedef." + activate.Key, true); + + // Add linedef flag translations + foreach (FlagTranslation f in General.Map.Config.LinedefFlagsTranslation) + { + foreach (string fn in f.Fields) + config.WriteSetting("managedfields.linedef." + fn, true); + } + + // Add thing flags + foreach (KeyValuePair flag in General.Map.Config.ThingFlags) + config.WriteSetting("managedfields.thing." + flag.Key, true); + + // Add thing flag translations + foreach (FlagTranslation f in General.Map.Config.ThingFlagsTranslation) + { + foreach (string fn in f.Fields) + config.WriteSetting("managedfields.thing." + fn, true); + } + + // Done + udmfcfgreader.Dispose(); + udmfcfg.Dispose(); + break; + } + } + } + + #endregion + + #region ================== Reading + + // This reads from a stream + public MapSet Read(MapSet map, Stream stream) + { + StreamReader reader = new StreamReader(stream, Encoding.ASCII); + Dictionary vertexlink; + Dictionary sectorlink; + UniversalParser textmap = new UniversalParser(); + textmap.StrictChecking = strictchecking; + + try + { + // Read UDMF from stream + textmap.InputConfiguration(reader.ReadToEnd()); + + // Check for errors + if (textmap.ErrorResult != 0) + { + // Show parse error + General.ShowErrorMessage("Error on line " + textmap.ErrorLine + " while parsing UDMF map data:\n" + textmap.ErrorDescription, MessageBoxButtons.OK); + } + else + { + // Read the map + vertexlink = ReadVertices(map, textmap); + sectorlink = ReadSectors(map, textmap); + ReadLinedefs(map, textmap, vertexlink, sectorlink); + ReadThings(map, textmap); + } + } + catch (Exception e) + { + General.ShowErrorMessage("Unexpected error reading UDMF map data. " + e.GetType().Name + ": " + e.Message, MessageBoxButtons.OK); + } + + return map; + } + + // This reads the things + private void ReadThings(MapSet map, UniversalParser textmap) + { + // Get list of entries + List collections = GetNamedCollections(textmap.Root, "thing"); + + // Go for all collections + map.SetCapacity(0, 0, 0, 0, map.Things.Count + collections.Count); + for (int i = 0; i < collections.Count; i++) + { + // Read fields + UniversalCollection c = collections[i]; + int[] args = new int[Linedef.NUM_ARGS]; + string where = "thing " + i; + float x = GetCollectionEntry(c, "x", true, 0.0f, where); + float y = GetCollectionEntry(c, "y", true, 0.0f, where); + float height = GetCollectionEntry(c, "height", false, 0.0f, where); + int tag = GetCollectionEntry(c, "id", false, 0, where); + int angledeg = GetCollectionEntry(c, "angle", false, 0, where); + int type = GetCollectionEntry(c, "type", true, 0, where); + int special = GetCollectionEntry(c, "special", false, 0, where); + args[0] = GetCollectionEntry(c, "arg0", false, 0, where); + args[1] = GetCollectionEntry(c, "arg1", false, 0, where); + args[2] = GetCollectionEntry(c, "arg2", false, 0, where); + args[3] = GetCollectionEntry(c, "arg3", false, 0, where); + args[4] = GetCollectionEntry(c, "arg4", false, 0, where); + + // Flags + Dictionary stringflags = new Dictionary(); + foreach (KeyValuePair flag in General.Map.Config.ThingFlags) + stringflags[flag.Key] = GetCollectionEntry(c, flag.Key, false, false, where); + foreach (FlagTranslation ft in General.Map.Config.ThingFlagsTranslation) + { + foreach (string field in ft.Fields) + stringflags[field] = GetCollectionEntry(c, field, false, false, where); + } + + // Create new item + Thing t = map.CreateThing(); + if (t != null) + { + t.Update(type, x, y, height, Angle2D.DoomToReal(angledeg), stringflags, tag, special, args); + + // Custom fields + ReadCustomFields(c, t, "thing"); + } + } + } + + // This reads the linedefs and sidedefs + private void ReadLinedefs(MapSet map, UniversalParser textmap, + Dictionary vertexlink, Dictionary sectorlink) + { + // Get list of entries + List linescolls = GetNamedCollections(textmap.Root, "linedef"); + List sidescolls = GetNamedCollections(textmap.Root, "sidedef"); + + // Go for all lines + map.SetCapacity(0, map.Linedefs.Count + linescolls.Count, map.Sidedefs.Count + sidescolls.Count, 0, 0); + for (int i = 0; i < linescolls.Count; i++) + { + // Read fields + UniversalCollection lc = linescolls[i]; + int[] args = new int[Linedef.NUM_ARGS]; + string where = "linedef " + i; + int tag = GetCollectionEntry(lc, "id", false, 0, where); + int v1 = GetCollectionEntry(lc, "v1", true, 0, where); + int v2 = GetCollectionEntry(lc, "v2", true, 0, where); + int special = GetCollectionEntry(lc, "special", false, 0, where); + int acti = GetCollectionEntry(lc, "activate", false, 0, where); // villsa 9/13/11 + int switchmask = GetCollectionEntry(lc, "switchmask", false, 0, where); // villsa 9/13/11 + args[0] = GetCollectionEntry(lc, "arg0", false, 0, where); + args[1] = GetCollectionEntry(lc, "arg1", false, 0, where); + args[2] = GetCollectionEntry(lc, "arg2", false, 0, where); + args[3] = GetCollectionEntry(lc, "arg3", false, 0, where); + args[4] = GetCollectionEntry(lc, "arg4", false, 0, where); + int s1 = GetCollectionEntry(lc, "sidefront", true, -1, where); + int s2 = GetCollectionEntry(lc, "sideback", false, -1, where); + + // Flags + Dictionary stringflags = new Dictionary(); + foreach (KeyValuePair flag in General.Map.Config.LinedefFlags) + stringflags[flag.Key] = GetCollectionEntry(lc, flag.Key, false, false, where); + + foreach (FlagTranslation ft in General.Map.Config.LinedefFlagsTranslation) + { + foreach (string field in ft.Fields) + stringflags[field] = GetCollectionEntry(lc, field, false, false, where); + } + + // Activations + foreach (LinedefActivateInfo activate in General.Map.Config.LinedefActivates) + stringflags[activate.Key] = GetCollectionEntry(lc, activate.Key, false, false, where); + + // Create new linedef + if (vertexlink.ContainsKey(v1) && vertexlink.ContainsKey(v2)) + { + // Check if not zero-length + if (Vector2D.ManhattanDistance(vertexlink[v1].Position, vertexlink[v2].Position) > 0.0001f) + { + Linedef l = map.CreateLinedef(vertexlink[v1], vertexlink[v2]); + if (l != null) + { + l.Update(stringflags, acti, tag, special, switchmask, args); + l.UpdateCache(); + + // Custom fields + ReadCustomFields(lc, l, "linedef"); + + // Read sidedefs and connect them to the line + if (s1 > -1) + { + if (s1 < sidescolls.Count) + ReadSidedef(map, sidescolls[s1], l, true, sectorlink, s1); + else + General.ErrorLogger.Add(ErrorType.Warning, "Linedef " + i + " references invalid front sidedef " + s1 + ". Sidedef has been removed."); + } + + if (s2 > -1) + { + if (s2 < sidescolls.Count) + ReadSidedef(map, sidescolls[s2], l, false, sectorlink, s2); + else + General.ErrorLogger.Add(ErrorType.Warning, "Linedef " + i + " references invalid back sidedef " + s1 + ". Sidedef has been removed."); + } + } + } + else + { + General.ErrorLogger.Add(ErrorType.Warning, "Linedef " + i + " is zero-length. Linedef has been removed."); + } + } + else + { + General.ErrorLogger.Add(ErrorType.Warning, "Linedef " + i + " references one or more invalid vertices. Linedef has been removed."); + } + } + } + + // This reads a single sidedef and connects it to the given linedef + private void ReadSidedef(MapSet map, UniversalCollection sc, Linedef ld, + bool front, Dictionary sectorlink, int index) + { + // Read fields + string where = "linedef " + ld.Index + (front ? " front sidedef " : " back sidedef ") + index; + int offsetx = GetCollectionEntry(sc, "offsetx", false, 0, where); + int offsety = GetCollectionEntry(sc, "offsety", false, 0, where); + string thigh = GetCollectionEntry(sc, "texturetop", false, "-", where); + string tlow = GetCollectionEntry(sc, "texturebottom", false, "-", where); + string tmid = GetCollectionEntry(sc, "texturemiddle", false, "-", where); + int sector = GetCollectionEntry(sc, "sector", true, 0, where); + + // Create sidedef + if (sectorlink.ContainsKey(sector)) + { + Sidedef s = map.CreateSidedef(ld, front, sectorlink[sector]); + if (s != null) + { + s.Update(offsetx, offsety, thigh, tmid, tlow); + + // Custom fields + ReadCustomFields(sc, s, "sidedef"); + } + } + else + { + General.ErrorLogger.Add(ErrorType.Warning, "Sidedef references invalid sector " + sector + ". Sidedef has been removed."); + } + } + + // This reads the sectors + private Dictionary ReadSectors(MapSet map, UniversalParser textmap) + { + Dictionary link; + + // Get list of entries + List collections = GetNamedCollections(textmap.Root, "sector"); + + // Create lookup table + link = new Dictionary(collections.Count); + + // Go for all collections + map.SetCapacity(0, 0, 0, map.Sectors.Count + collections.Count, 0); + for (int i = 0; i < collections.Count; i++) + { + // Read fields + UniversalCollection c = collections[i]; + string where = "sector " + i; + int[] colors = new int[Sector.NUM_COLORS]; + int hfloor = GetCollectionEntry(c, "heightfloor", false, 0, where); + int hceil = GetCollectionEntry(c, "heightceiling", false, 0, where); + string tfloor = GetCollectionEntry(c, "texturefloor", true, "-", where); + string tceil = GetCollectionEntry(c, "textureceiling", true, "-", where); + int bright = GetCollectionEntry(c, "lightlevel", false, 160, where); + int special = GetCollectionEntry(c, "special", false, 0, where); + int tag = GetCollectionEntry(c, "id", false, 0, where); + + // villsa 9/14/11 (builder64) + colors[0] = GetCollectionEntry(c, "color1", false, 0, where); + colors[1] = GetCollectionEntry(c, "color2", false, 0, where); + colors[2] = GetCollectionEntry(c, "color3", false, 0, where); + colors[3] = GetCollectionEntry(c, "color4", false, 0, where); + colors[4] = GetCollectionEntry(c, "color5", false, 0, where); + + // villsa 9/13/11 - Flags + Dictionary stringflags = new Dictionary(); + foreach (KeyValuePair flag in General.Map.Config.SectorFlags) + stringflags[flag.Key] = GetCollectionEntry(c, flag.Key, false, false, where); + + // Create new item + Sector s = map.CreateSector(); + if (s != null) + { + s.Update(stringflags, hfloor, hceil, tfloor, tceil, special, tag, colors); + + // Custom fields + ReadCustomFields(c, s, "sector"); + + // Add it to the lookup table + link.Add(i, s); + } + } + + // Return lookup table + return link; + } + + // This reads the vertices + private Dictionary ReadVertices(MapSet map, UniversalParser textmap) + { + Dictionary link; + + // Get list of entries + List collections = GetNamedCollections(textmap.Root, "vertex"); + + // Create lookup table + link = new Dictionary(collections.Count); + + // Go for all collections + map.SetCapacity(map.Vertices.Count + collections.Count, 0, 0, 0, 0); + for (int i = 0; i < collections.Count; i++) + { + // Read fields + UniversalCollection c = collections[i]; + string where = "vertex " + i; + float x = GetCollectionEntry(c, "x", true, 0.0f, where); + float y = GetCollectionEntry(c, "y", true, 0.0f, where); + + // Create new item + Vertex v = map.CreateVertex(new Vector2D(x, y)); + if (v != null) + { + // Custom fields + ReadCustomFields(c, v, "vertex"); + + // Add it to the lookup table + link.Add(i, v); + } + } + + // Return lookup table + return link; + } + + // This reads custom fields from a collection and adds them to a map element + private void ReadCustomFields(UniversalCollection collection, MapElement element, string elementname) + { + element.Fields.BeforeFieldsChange(); + + // Go for all the elements in the collection + foreach (UniversalEntry e in collection) + { + // Check if not a managed field + if (!config.SettingExists("managedfields." + elementname + "." + e.Key)) + { + int type = (int)UniversalType.Integer; + + // Determine default type + if (e.Value.GetType() == typeof(int)) type = (int)UniversalType.Integer; + else if (e.Value.GetType() == typeof(float)) type = (int)UniversalType.Float; + else if (e.Value.GetType() == typeof(bool)) type = (int)UniversalType.Boolean; + else if (e.Value.GetType() == typeof(string)) type = (int)UniversalType.String; + + // Try to find the type from configuration + if (setknowncustomtypes) + type = General.Map.Options.GetUniversalFieldType(elementname, e.Key, type); + + // Make custom field + element.Fields[e.Key] = new UniValue(type, e.Value); + } + } + } + + // This validates and returns an entry + private T GetCollectionEntry(UniversalCollection c, string entryname, bool required, T defaultvalue, string where) + { + T result = default(T); + bool found = false; + + // Find the entry + foreach (UniversalEntry e in c) + { + // Check if matches + if (e.Key == entryname) + { + // Let's be kind and cast any int to a float if needed + if ((typeof(T) == typeof(float)) && + (e.Value.GetType() == typeof(int))) + { + // Make it a float + object fvalue = (float)(int)e.Value; + result = (T)fvalue; + } + else + { + // Verify type + e.ValidateType(typeof(T)); + + // Found it! + result = (T)e.Value; + } + + // Done + found = true; + } + } + + // Not found? + if (!found) + { + // Report error when entry is required! + if (required) + General.ErrorLogger.Add(ErrorType.Error, "Error while reading UDMF map data: Missing required field '" + entryname + "' at " + where + "."); + + // Make default entry + result = defaultvalue; + } + + // Return result + return result; + } + + // This makes a list of all collections with the given name + private List GetNamedCollections(UniversalCollection collection, string entryname) + { + List list = new List(); + + // Make list + foreach (UniversalEntry e in collection) + if ((e.Value is UniversalCollection) && (e.Key == entryname)) list.Add(e.Value as UniversalCollection); + + return list; + } + + #endregion + } +} + diff --git a/Source/Core/IO/UniversalStreamWriter.cs b/Source/Core/IO/UniversalStreamWriter.cs new file mode 100644 index 0000000..d5ff592 --- /dev/null +++ b/Source/Core/IO/UniversalStreamWriter.cs @@ -0,0 +1,341 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Geometry; +using System.Windows.Forms; +using CodeImp.DoomBuilder.Config; +using CodeImp.DoomBuilder.Types; + +#endregion + +namespace CodeImp.DoomBuilder.IO +{ + internal class UniversalStreamWriter + { + #region ================== Constants + + // Name of the UDMF configuration file + private const string UDMF_CONFIG_NAME = "UDMF.cfg"; + + #endregion + + #region ================== Variables + + private Configuration config; + private bool remembercustomtypes; + + #endregion + + #region ================== Properties + + public bool RememberCustomTypes { get { return remembercustomtypes; } set { remembercustomtypes = value; } } + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + public UniversalStreamWriter() + { + // Make configurations + config = new Configuration(); + + // Find a resource named UDMF.cfg + string[] resnames = General.ThisAssembly.GetManifestResourceNames(); + foreach (string rn in resnames) + { + // Found it? + if (rn.EndsWith(UDMF_CONFIG_NAME, StringComparison.InvariantCultureIgnoreCase)) + { + // Get a stream from the resource + Stream udmfcfg = General.ThisAssembly.GetManifestResourceStream(rn); + StreamReader udmfcfgreader = new StreamReader(udmfcfg, Encoding.ASCII); + + // Load configuration from stream + config.InputConfiguration(udmfcfgreader.ReadToEnd()); + + // Now we add the linedef flags, activations and thing flags + // to this list, so that these don't show up in the custom + // fields list either. We use true as dummy value (it has no meaning) + + // Add linedef flags + foreach (KeyValuePair flag in General.Map.Config.LinedefFlags) + config.WriteSetting("managedfields.linedef." + flag.Key, true); + + // Add linedef activations + foreach (LinedefActivateInfo activate in General.Map.Config.LinedefActivates) + config.WriteSetting("managedfields.linedef." + activate.Key, true); + + // Add thing flags + foreach (KeyValuePair flag in General.Map.Config.ThingFlags) + config.WriteSetting("managedfields.thing." + flag.Key, true); + + // Done + udmfcfgreader.Dispose(); + udmfcfg.Dispose(); + break; + } + } + } + + #endregion + + #region ================== Writing + + // This writes the structures to a stream + // writenamespace may be null to omit writing the namespace to the stream + public void Write(MapSet map, Stream stream, string writenamespace) + { + Write(map.Vertices, map.Linedefs, map.Sidedefs, map.Sectors, map.Things, stream, writenamespace); + } + + // This writes the structures to a stream + // NOTE: writenamespace may be null to omit writing the namespace to the stream. + // NOTE: The given structures must be complete, with the exception of the sidedefs. + // If there are missing sidedefs, their reference will be removed from the linedefs. + public void Write(ICollection vertices, ICollection linedefs, + ICollection sidedefs, ICollection sectors, + ICollection things, Stream stream, string writenamespace) + { + UniversalParser textmap = new UniversalParser(); + + // Begin with fields that must be at the top + if (writenamespace != null) textmap.Root.Add("namespace", writenamespace); + + Dictionary vertexids = new Dictionary(); + Dictionary sidedefids = new Dictionary(); + Dictionary sectorids = new Dictionary(); + + // Index the elements in the data structures + foreach (Vertex v in vertices) vertexids.Add(v, vertexids.Count); + foreach (Sidedef sd in sidedefs) sidedefids.Add(sd, sidedefids.Count); + foreach (Sector s in sectors) sectorids.Add(s, sectorids.Count); + + // If we write the custom field types again, then forget + // all previous field types (this gets rid of unused field types) + if (remembercustomtypes) General.Map.Options.ForgetUniversalFieldTypes(); + + // Write the data structures to textmap + WriteVertices(vertices, textmap); + WriteLinedefs(linedefs, textmap, sidedefids, vertexids); + WriteSidedefs(sidedefs, textmap, sectorids); + WriteSectors(sectors, textmap); + WriteThings(things, textmap); + + // Get the textmap as string + string textmapstr = textmap.OutputConfiguration(); + + // Write to stream + StreamWriter writer = new StreamWriter(stream, Encoding.ASCII); + writer.Write(textmapstr); + writer.Flush(); + } + + // This adds vertices + private void WriteVertices(ICollection vertices, UniversalParser textmap) + { + // Go for all vertices + foreach (Vertex v in vertices) + { + // Make collection + UniversalCollection coll = new UniversalCollection(); + coll.Add("x", v.Position.x); + coll.Add("y", v.Position.y); + coll.Comment = v.Index.ToString(); + + // Add custom fields + AddCustomFields(v, "vertex", coll); + + // Store + textmap.Root.Add("vertex", coll); + } + } + + // This adds linedefs + private void WriteLinedefs(ICollection linedefs, UniversalParser textmap, IDictionary sidedefids, IDictionary vertexids) + { + // Go for all linedefs + foreach (Linedef l in linedefs) + { + // Make collection + UniversalCollection coll = new UniversalCollection(); + if (l.Tag != 0) coll.Add("id", l.Tag); + coll.Add("v1", vertexids[l.Start]); + coll.Add("v2", vertexids[l.End]); + coll.Comment = l.Index.ToString(); + + // Sidedef references + if ((l.Front != null) && sidedefids.ContainsKey(l.Front)) + coll.Add("sidefront", sidedefids[l.Front]); + else + coll.Add("sidefront", -1); + + if ((l.Back != null) && sidedefids.ContainsKey(l.Back)) + coll.Add("sideback", sidedefids[l.Back]); + else + coll.Add("sideback", -1); + + // Special + if (l.Action != 0) coll.Add("special", l.Action); + if (l.Activate != 0) coll.Add("activate", l.Activate); // villsa 9/13/11 + if (l.SwitchMask != 0) coll.Add("switchmask", l.SwitchMask); // villsa 9/13/11 + if (l.Args[0] != 0) coll.Add("arg0", l.Args[0]); + if (l.Args[1] != 0) coll.Add("arg1", l.Args[1]); + if (l.Args[2] != 0) coll.Add("arg2", l.Args[2]); + if (l.Args[3] != 0) coll.Add("arg3", l.Args[3]); + if (l.Args[4] != 0) coll.Add("arg4", l.Args[4]); + + // Flags + foreach (KeyValuePair flag in l.Flags) + if (flag.Value) coll.Add(flag.Key, flag.Value); + + // Add custom fields + AddCustomFields(l, "linedef", coll); + + // Store + textmap.Root.Add("linedef", coll); + } + } + + // This adds sidedefs + private void WriteSidedefs(ICollection sidedefs, UniversalParser textmap, IDictionary sectorids) + { + // Go for all sidedefs + foreach (Sidedef s in sidedefs) + { + int sectorid = (s.Sector != null) ? sectorids[s.Sector] : -1; + + // Make collection + UniversalCollection coll = new UniversalCollection(); + if (s.OffsetX != 0) coll.Add("offsetx", s.OffsetX); + if (s.OffsetY != 0) coll.Add("offsety", s.OffsetY); + if (s.LongHighTexture != MapSet.EmptyLongName) coll.Add("texturetop", s.HighTexture); + if (s.LongLowTexture != MapSet.EmptyLongName) coll.Add("texturebottom", s.LowTexture); + if (s.LongMiddleTexture != MapSet.EmptyLongName) coll.Add("texturemiddle", s.MiddleTexture); + coll.Add("sector", sectorids[s.Sector]); + coll.Comment = s.Index.ToString(); + + // Add custom fields + AddCustomFields(s, "sidedef", coll); + + // Store + textmap.Root.Add("sidedef", coll); + } + } + + // This adds sectors + private void WriteSectors(ICollection sectors, UniversalParser textmap) + { + // Go for all sectors + foreach (Sector s in sectors) + { + // Make collection + UniversalCollection coll = new UniversalCollection(); + coll.Add("heightfloor", s.FloorHeight); + coll.Add("heightceiling", s.CeilHeight); + coll.Add("texturefloor", s.FloorTexture); + coll.Add("textureceiling", s.CeilTexture); + coll.Add("lightlevel", s.Brightness); + if (s.Effect != 0) coll.Add("special", s.Effect); + if (s.Tag != 0) coll.Add("id", s.Tag); + + // villsa 9/14/11 (builder64) + coll.Add("color1", s.FloorColor.color.ToInt()); + coll.Add("color2", s.CeilColor.color.ToInt()); + coll.Add("color3", s.ThingColor.color.ToInt()); + coll.Add("color4", s.TopColor.color.ToInt()); + coll.Add("color5", s.LowerColor.color.ToInt()); + + // villsa 9/13/11 - Flags + foreach (KeyValuePair flag in s.Flags) + if (flag.Value) coll.Add(flag.Key, flag.Value); + + coll.Comment = s.Index.ToString(); + + // Add custom fields + AddCustomFields(s, "sector", coll); + + // Store + textmap.Root.Add("sector", coll); + } + } + + // This adds things + private void WriteThings(ICollection things, UniversalParser textmap) + { + // Go for all things + foreach (Thing t in things) + { + // Make collection + UniversalCollection coll = new UniversalCollection(); + if (t.Tag != 0) coll.Add("id", t.Tag); + coll.Add("x", t.Position.x); + coll.Add("y", t.Position.y); + if (t.Position.z != 0.0f) coll.Add("height", (float)t.Position.z); + coll.Add("angle", Angle2D.RealToDoom(t.Angle)); + coll.Add("type", t.Type); + if (t.Action != 0) coll.Add("special", t.Action); + if (t.Args[0] != 0) coll.Add("arg0", t.Args[0]); + if (t.Args[1] != 0) coll.Add("arg1", t.Args[1]); + if (t.Args[2] != 0) coll.Add("arg2", t.Args[2]); + if (t.Args[3] != 0) coll.Add("arg3", t.Args[3]); + if (t.Args[4] != 0) coll.Add("arg4", t.Args[4]); + coll.Comment = t.Index.ToString(); + + // Flags + foreach (KeyValuePair flag in t.Flags) + if (flag.Value) coll.Add(flag.Key, flag.Value); + + // Add custom fields + AddCustomFields(t, "thing", coll); + + // Store + textmap.Root.Add("thing", coll); + } + } + + // This adds custom fields from a map element to a collection + private void AddCustomFields(MapElement element, string elementname, UniversalCollection collection) + { + // Add custom fields + foreach (KeyValuePair f in element.Fields) + { + // Not a managed field? + if (!config.SettingExists("managedfields." + elementname + "." + f.Key)) + { + // Add type information to DBS file for map + if (remembercustomtypes) + General.Map.Options.SetUniversalFieldType(elementname, f.Key, f.Value.Type); + + // Store field + collection.Add(f.Key, f.Value.Value); + } + } + } + + #endregion + } +} + diff --git a/Source/Core/IO/UnknownImageReader.cs b/Source/Core/IO/UnknownImageReader.cs new file mode 100644 index 0000000..f795bb5 --- /dev/null +++ b/Source/Core/IO/UnknownImageReader.cs @@ -0,0 +1,115 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Geometry; +using System.Drawing; +using CodeImp.DoomBuilder.Data; +using CodeImp.DoomBuilder.Rendering; +using System.Drawing.Imaging; + +#endregion + +namespace CodeImp.DoomBuilder.IO +{ + internal unsafe class UnknownImageReader : IImageReader + { + #region ================== Constructor / Disposer + + // Constructor + public UnknownImageReader() + { + // We have no destructor + GC.SuppressFinalize(this); + } + + #endregion + + #region ================== Methods + + // This creates a Bitmap from the given data + // Returns null on failure + public Bitmap ReadAsBitmap(Stream stream, out int offsetx, out int offsety) + { + offsetx = int.MinValue; + offsety = int.MinValue; + return ReadAsBitmap(stream); + } + + // This reads the image and returns a Bitmap + public Bitmap ReadAsBitmap(Stream stream) + { + return new Bitmap(CodeImp.DoomBuilder.Properties.Resources.Failed); + } + + // This reads the image and returns a Bitmap + public static Bitmap ReadAsBitmap() + { + return new Bitmap(CodeImp.DoomBuilder.Properties.Resources.Failed); + } + + // This draws the picture to the given pixel color data + // Throws exception on failure + public unsafe void DrawToPixelData(Stream stream, PixelColor* target, int targetwidth, int targetheight, int x, int y) + { + Bitmap bmp; + BitmapData bmpdata; + PixelColor* pixels; + int ox, oy, tx, ty; + int width, height; + + // Get bitmap + bmp = ReadAsBitmap(stream); + width = bmp.Size.Width; + height = bmp.Size.Height; + + // Lock bitmap pixels + bmpdata = bmp.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb); + pixels = (PixelColor*)bmpdata.Scan0.ToPointer(); + + // Go for all pixels in the original image + for (ox = 0; ox < width; ox++) + { + for (oy = 0; oy < height; oy++) + { + // Copy this pixel? + if (pixels[oy * width + ox].a > 0.5f) + { + // Calculate target pixel and copy when within bounds + tx = x + ox; + ty = y + oy; + if ((tx >= 0) && (tx < targetwidth) && (ty >= 0) && (ty < targetheight)) + target[ty * targetwidth + tx] = pixels[oy * width + ox]; + } + } + } + + // Done + bmp.UnlockBits(bmpdata); + bmp.Dispose(); + } + + #endregion + } +} diff --git a/Source/Core/IO/WAD.cs b/Source/Core/IO/WAD.cs new file mode 100644 index 0000000..ae08391 --- /dev/null +++ b/Source/Core/IO/WAD.cs @@ -0,0 +1,410 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.IO; + +#endregion + +namespace CodeImp.DoomBuilder.IO +{ + internal class WAD + { + #region ================== Constants + + // WAD types + public const string TYPE_IWAD = "IWAD"; + public const string TYPE_PWAD = "PWAD"; + + // Encoder + public static readonly Encoding ENCODING = Encoding.ASCII; + + #endregion + + #region ================== Variables + + // File objects + private string filename; + private FileStream file; + private BinaryReader reader; + private BinaryWriter writer; + + // Header + private string type; + private int numlumps; + private int lumpsoffset; + + // Lumps + private List lumps; + + // Status + private bool isreadonly = false; + private bool isdisposed = false; + + #endregion + + #region ================== Properties + + public string Filename { get { return filename; } } + public string Type { get { return type; } } + public Encoding Encoding { get { return ENCODING; } } + public bool IsReadOnly { get { return isreadonly; } } + public bool IsDisposed { get { return isdisposed; } } + public List Lumps { get { return lumps; } } + + #endregion + + #region ================== Constructor / Disposer + + // Constructor to open or create a WAD file + public WAD(string pathfilename) + { + // Initialize + this.isreadonly = false; + this.Open(pathfilename); + } + + // Constructor to open or create a WAD file + public WAD(string pathfilename, bool openreadonly) + { + // Initialize + this.isreadonly = openreadonly; + this.Open(pathfilename); + } + + // Destructor + ~WAD() + { + // Make sure everything is disposed + this.Dispose(); + } + + // Disposer + public void Dispose() + { + // Not already disposed? + if (!isdisposed) + { + // Flush all changes + Flush(); + + // Clean up + if (lumps != null) foreach (Lump l in lumps) l.Dispose(); + if (writer != null) writer.Close(); + if (reader != null) reader.Close(); + if (file != null) file.Dispose(); + + // Done + isdisposed = true; + } + } + + #endregion + + #region ================== IO + + // Open a WAD file + private void Open(string pathfilename) + { + FileAccess access; + FileShare share; + + // Determine if opening for read only + if (isreadonly) + { + // Read only + access = FileAccess.Read; + share = FileShare.ReadWrite; + } + else + { + // Private access + access = FileAccess.ReadWrite; + share = FileShare.Read; + } + + // Keep filename + filename = pathfilename; + + // Open the file stream + file = File.Open(pathfilename, FileMode.OpenOrCreate, access, share); + + // Create file handling tools + reader = new BinaryReader(file, ENCODING); + if (!isreadonly) writer = new BinaryWriter(file, ENCODING); + + // Is the WAD file zero length? + if (file.Length == 0) + { + // Create the headers in file + CreateHeaders(); + } + else + { + // Read information from file + ReadHeaders(); + } + } + + // This creates new file headers + private void CreateHeaders() + { + // Default settings + type = TYPE_PWAD; + lumpsoffset = 12; + + // New lumps array + lumps = new List(numlumps); + + // Write the headers + WriteHeaders(); + } + + // This reads the WAD header and lumps table + private void ReadHeaders() + { + int offset, length; + byte[] fixedname; + + // Make sure the write is finished writing + if (!isreadonly) writer.Flush(); + + // Seek to beginning + file.Seek(0, SeekOrigin.Begin); + + // Read WAD type + type = ENCODING.GetString(reader.ReadBytes(4)); + + // Number of lumps + numlumps = reader.ReadInt32(); + if (numlumps < 0) throw new IOException("Invalid number of lumps in wad file."); + + // Lumps table offset + lumpsoffset = reader.ReadInt32(); + if (lumpsoffset < 0) throw new IOException("Invalid lumps offset in wad file."); + + // Seek to the lumps table + file.Seek(lumpsoffset, SeekOrigin.Begin); + + // Dispose old lumps and create new list + if (lumps != null) foreach (Lump l in lumps) l.Dispose(); + lumps = new List(numlumps); + + // Go for all lumps + for (int i = 0; i < numlumps; i++) + { + // Read lump information + offset = reader.ReadInt32(); + length = reader.ReadInt32(); + fixedname = reader.ReadBytes(8); + + // Create the lump + lumps.Add(new Lump(file, this, fixedname, offset, length)); + } + } + + // This reads the WAD header and lumps table + public void WriteHeaders() + { + // Seek to beginning + file.Seek(0, SeekOrigin.Begin); + + // Write WAD type + writer.Write(ENCODING.GetBytes(type)); + + // Number of lumps + writer.Write(numlumps); + + // Lumps table offset + writer.Write(lumpsoffset); + + // Seek to the lumps table + file.Seek(lumpsoffset, SeekOrigin.Begin); + + // Go for all lumps + for (int i = 0; i < lumps.Count; i++) + { + // Write lump information + writer.Write(lumps[i].Offset); + writer.Write(lumps[i].Length); + writer.Write(lumps[i].FixedName); + } + } + + // This flushes writing changes + public void Flush() + { + // Only possible when not read-only + if (!isreadonly) + { + // Flush writing changes + if (writer != null) writer.Flush(); + if (file != null) file.Flush(); + } + } + + #endregion + + #region ================== Lumps + + // This creates a new lump in the WAD file + public Lump Insert(string name, int position, int datalength) + { + Lump lump; + + // We will be adding a lump + numlumps++; + + // Extend the file + file.SetLength(file.Length + datalength + 16); + + // Create the lump + lump = new Lump(file, this, Lump.MakeFixedName(name, ENCODING), lumpsoffset, datalength); + lumps.Insert(position, lump); + + // Advance lumps table offset + lumpsoffset += datalength; + + // Write the new headers + WriteHeaders(); + + // Return the new lump + return lump; + } + + // This removes a lump from the WAD file by index + public void RemoveAt(int index) + { + Lump l; + + // Remove from list + l = lumps[index]; + lumps.RemoveAt(index); + l.Dispose(); + numlumps--; + + // Write the new headers + WriteHeaders(); + } + + // This removes a lump from the WAD file + public void Remove(Lump lump) + { + // Remove from list + lumps.Remove(lump); + lump.Dispose(); + numlumps--; + + // Write the new headers + WriteHeaders(); + } + + // This finds a lump by name, returns null when not found + public Lump FindLump(string name) + { + int index = FindLumpIndex(name); + if (index == -1) + return null; + else + return lumps[index]; + } + + // This finds a lump by name, returns null when not found + public Lump FindLump(string name, int start) + { + int index = FindLumpIndex(name, start); + if (index == -1) + return null; + else + return lumps[index]; + } + + // This finds a lump by name, returns null when not found + public Lump FindLump(string name, int start, int end) + { + int index = FindLumpIndex(name, start, end); + if (index == -1) + return null; + else + return lumps[index]; + } + + // This finds a lump by name, returns -1 when not found + public int FindLumpIndex(string name) + { + // Do search + return FindLumpIndex(name, 0, lumps.Count - 1); + } + + // This finds a lump by name, returns -1 when not found + public int FindLumpIndex(string name, int start) + { + // Do search + return FindLumpIndex(name, start, lumps.Count - 1); + } + + // This finds a lump by name, returns -1 when not found + public int FindLumpIndex(string name, int start, int end) + { + byte[] fixedname; + long longname = Lump.MakeLongName(name); + + // Fix end when it exceeds length + if (end > (lumps.Count - 1)) end = lumps.Count - 1; + + // Make sure name is in uppercase + name = name.ToUpperInvariant(); + + // Make fixed name + fixedname = Lump.MakeFixedName(name, ENCODING); + + // Loop through the lumps + for (int i = start; i <= end; i++) + { + /* + // Check if first byte matches + if(lumps[i].FixedName[0] == fixedname[0]) + { + // Check if the lump name matches + if(lumps[i].Name.StartsWith(name, false, CultureInfo.InvariantCulture)) + { + // Found the lump! + return i; + } + } + */ + + // Check if the lump name matches + if (lumps[i].LongName == longname) + { + // Found the lump! + return i; + } + } + + // Nothing found + return -1; + } + + #endregion + } +} diff --git a/Source/Core/Map/BlockEntry.cs b/Source/Core/Map/BlockEntry.cs new file mode 100644 index 0000000..1be4b02 --- /dev/null +++ b/Source/Core/Map/BlockEntry.cs @@ -0,0 +1,72 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using CodeImp.DoomBuilder.Windows; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Actions; +using CodeImp.DoomBuilder.Geometry; +using System.Drawing; +using SlimDX; +using CodeImp.DoomBuilder.Editing; + +#endregion + +namespace CodeImp.DoomBuilder.Map +{ + public class BlockEntry + { + #region ================== Variables + + // Members + private List lines; + private List things; + private List sectors; + + #endregion + + #region ================== Properties + + public List Lines { get { return lines; } } + public List Things { get { return things; } } + public List Sectors { get { return sectors; } } + + #endregion + + #region ================== Constructor + + // Constructor for empty block + public BlockEntry() + { + lines = new List(2); + things = new List(2); + sectors = new List(2); + } + + #endregion + } +} diff --git a/Source/Core/Map/BlockMap.cs b/Source/Core/Map/BlockMap.cs new file mode 100644 index 0000000..101c9b8 --- /dev/null +++ b/Source/Core/Map/BlockMap.cs @@ -0,0 +1,514 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using CodeImp.DoomBuilder.Windows; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Actions; +using CodeImp.DoomBuilder.Geometry; +using System.Drawing; +using SlimDX; +using CodeImp.DoomBuilder.Editing; + +#endregion + +namespace CodeImp.DoomBuilder.Map +{ + public class BlockMap where BE : BlockEntry, new() + { + #region ================== Constants + + #endregion + + #region ================== Variables + + // Blocks + protected BE[,] blockmap; + protected int blocksizeshift; + protected int blocksize; + protected Size size; + protected RectangleF range; + protected Vector2D rangelefttop; + + // State + private bool isdisposed; + + #endregion + + #region ================== Properties + + public bool IsDisposed { get { return isdisposed; } } + public Size Size { get { return size; } } + public RectangleF Range { get { return range; } } + public int BlockSize { get { return blocksize; } } + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + public BlockMap(RectangleF range) + { + Initialize(range, 128); + } + + // Constructor + public BlockMap(RectangleF range, int blocksize) + { + Initialize(range, blocksize); + } + + // This initializes the blockmap + private void Initialize(RectangleF range, int blocksize) + { + // Initialize + this.range = range; + this.blocksizeshift = General.BitsForInt(blocksize); + this.blocksize = 1 << blocksizeshift; + if ((this.blocksize != blocksize) || (this.blocksize <= 1)) throw new ArgumentException("Block size must be a power of 2 greater than 1"); + rangelefttop = new Vector2D(range.Left, range.Top); + Point lefttop = new Point((int)range.Left >> blocksizeshift, (int)range.Top >> blocksizeshift); + Point rightbottom = new Point((int)range.Right >> blocksizeshift, (int)range.Bottom >> blocksizeshift); + size = new Size((rightbottom.X - lefttop.X) + 1, (rightbottom.Y - lefttop.Y) + 1); + blockmap = new BE[size.Width, size.Height]; + Clear(); + } + + // Disposer + public void Dispose() + { + // Not already disposed? + if (!isdisposed) + { + // Clean up + blockmap = null; + + // Done + isdisposed = true; + } + } + + #endregion + + #region ================== Methods + + // This returns the block coordinates + protected Point GetBlockCoordinates(Vector2D v) + { + return new Point((int)(v.x - range.Left) >> blocksizeshift, + (int)(v.y - range.Top) >> blocksizeshift); + } + + // This returns the block center in world coordinates + protected Vector2D GetBlockCenter(Point p) + { + return new Vector2D((float)((p.X << blocksizeshift) + (blocksize >> 1)) + range.Left, + (float)((p.Y << blocksizeshift) + (blocksize >> 1)) + range.Top); + } + + // This returns true when the given block is inside range + protected bool IsInRange(Point p) + { + return (p.X >= 0) && (p.X < size.Width) && (p.Y >= 0) && (p.Y < size.Height); + } + + // This returns true when the given block is inside range + public bool IsInRange(Vector2D p) + { + return (p.x >= range.Left) && (p.x < range.Right) && (p.y >= range.Top) && (p.y < range.Bottom); + } + + // This crops a point into the range + protected Point CropToRange(Point p) + { + return new Point(Math.Min(Math.Max(p.X, 0), size.Width - 1), + Math.Min(Math.Max(p.Y, 0), size.Height - 1)); + } + + // This crops a point into the range + protected int CropToRangeX(int x) + { + return Math.Min(Math.Max(x, 0), size.Width - 1); + } + + // This crops a point into the range + protected int CropToRangeY(int y) + { + return Math.Min(Math.Max(y, 0), size.Height - 1); + } + + // This clears the blockmap + public virtual void Clear() + { + for (int x = 0; x < size.Width; x++) + { + for (int y = 0; y < size.Height; y++) + { + blockmap[x, y] = new BE(); + } + } + } + + // This returns a blocks at the given coordinates, if any + // Returns null when out of range + public virtual BE GetBlockAt(Vector2D pos) + { + // Calculate block coordinates + Point p = GetBlockCoordinates(pos); + return IsInRange(p) ? blockmap[p.X, p.Y] : null; + } + + // This returns a range of blocks in a square + public virtual List GetSquareRange(RectangleF rect) + { + // Calculate block coordinates + Point lt = GetBlockCoordinates(new Vector2D(rect.Left, rect.Top)); + Point rb = GetBlockCoordinates(new Vector2D(rect.Right, rect.Bottom)); + + // Crop coordinates to range + lt = CropToRange(lt); + rb = CropToRange(rb); + + // Go through the range to make a list + int entriescount = ((rb.X - lt.X) + 1) * ((rb.Y - lt.Y) + 1); + List entries = new List(entriescount); + for (int x = lt.X; x <= rb.X; x++) + { + for (int y = lt.Y; y <= rb.Y; y++) + { + entries.Add(blockmap[x, y]); + } + } + + // Return list + return entries; + } + + // This returns all blocks along the given line + public virtual List GetLineBlocks(Vector2D v1, Vector2D v2) + { + float deltax, deltay; + float posx, posy; + Point pos, end; + int dirx, diry; + + // Estimate number of blocks we will go through and create list + int entriescount = (int)(Vector2D.ManhattanDistance(v1, v2) * 2.0f) / blocksize; + List entries = new List(entriescount); + + // Find start and end block + pos = GetBlockCoordinates(v1); + end = GetBlockCoordinates(v2); + v1 -= rangelefttop; + v2 -= rangelefttop; + + // Horizontal straight line? + if (pos.Y == end.Y) + { + // Simple loop + pos.X = CropToRangeX(pos.X); + end.X = CropToRangeX(end.X); + if (IsInRange(new Point(pos.X, pos.Y))) + { + dirx = Math.Sign(v2.x - v1.x); + if (dirx != 0) + { + for (int x = pos.X; x != end.X; x += dirx) + { + entries.Add(blockmap[x, pos.Y]); + } + } + entries.Add(blockmap[end.X, end.Y]); + } + } + // Vertical straight line? + else if (pos.X == end.X) + { + // Simple loop + pos.Y = CropToRangeY(pos.Y); + end.Y = CropToRangeY(end.Y); + if (IsInRange(new Point(pos.X, pos.Y))) + { + diry = Math.Sign(v2.y - v1.y); + if (diry != 0) + { + for (int y = pos.Y; y != end.Y; y += diry) + { + entries.Add(blockmap[pos.X, y]); + } + } + entries.Add(blockmap[end.X, end.Y]); + } + } + else + { + // Add this block + if (IsInRange(pos)) entries.Add(blockmap[pos.X, pos.Y]); + + // Moving outside the block? + if (pos != end) + { + // Calculate current block edges + float cl = pos.X * blocksize; + float cr = (pos.X + 1) * blocksize; + float ct = pos.Y * blocksize; + float cb = (pos.Y + 1) * blocksize; + + // Line directions + dirx = Math.Sign(v2.x - v1.x); + diry = Math.Sign(v2.y - v1.y); + + // Calculate offset and delta movement over x + if (dirx >= 0) + { + posx = (cr - v1.x) / (v2.x - v1.x); + deltax = blocksize / (v2.x - v1.x); + } + else + { + // Calculate offset and delta movement over x + posx = (v1.x - cl) / (v1.x - v2.x); + deltax = blocksize / (v1.x - v2.x); + } + + // Calculate offset and delta movement over y + if (diry >= 0) + { + posy = (cb - v1.y) / (v2.y - v1.y); + deltay = blocksize / (v2.y - v1.y); + } + else + { + posy = (v1.y - ct) / (v1.y - v2.y); + deltay = blocksize / (v1.y - v2.y); + } + + // Continue while not reached the end + while (pos != end) + { + // Check in which direction to move + if (posx < posy) + { + // Move horizontally + posx += deltax; + if (pos.X != end.X) pos.X += dirx; + } + else + { + // Move vertically + posy += deltay; + if (pos.Y != end.Y) pos.Y += diry; + } + + // Add lines to this block + if (IsInRange(pos)) entries.Add(blockmap[pos.X, pos.Y]); + } + } + } + + // Return list + return entries; + } + + // This puts a thing in the blockmap + public virtual void AddThingsSet(ICollection things) + { + foreach (Thing t in things) AddThing(t); + } + + // This puts a thing in the blockmap + public virtual void AddThing(Thing t) + { + Point p = GetBlockCoordinates(t.Position); + if (IsInRange(p)) blockmap[p.X, p.Y].Things.Add(t); + } + + // This puts a secotr in the blockmap + public virtual void AddSectorsSet(ICollection sectors) + { + foreach (Sector s in sectors) AddSector(s); + } + + // This puts a sector in the blockmap + public virtual void AddSector(Sector s) + { + Point p1 = GetBlockCoordinates(new Vector2D(s.BBox.Left, s.BBox.Top)); + Point p2 = GetBlockCoordinates(new Vector2D(s.BBox.Right, s.BBox.Bottom)); + p1 = CropToRange(p1); + p2 = CropToRange(p2); + for (int x = p1.X; x <= p2.X; x++) + { + for (int y = p1.Y; y <= p2.Y; y++) + { + blockmap[x, y].Sectors.Add(s); + } + } + } + + // This puts a whole set of linedefs in the blocks they cross + public virtual void AddLinedefsSet(ICollection lines) + { + foreach (Linedef l in lines) AddLinedef(l); + } + + // This puts a single linedef in all blocks it crosses + public virtual void AddLinedef(Linedef line) + { + Vector2D v1, v2; + float deltax, deltay; + float posx, posy; + Point pos, end; + int dirx, diry; + + // Get coordinates + v1 = line.Start.Position; + v2 = line.End.Position; + + // Find start and end block + pos = GetBlockCoordinates(v1); + end = GetBlockCoordinates(v2); + v1 -= rangelefttop; + v2 -= rangelefttop; + + // Horizontal straight line? + if (pos.Y == end.Y) + { + // Simple loop + pos.X = CropToRangeX(pos.X); + end.X = CropToRangeX(end.X); + if (IsInRange(new Point(pos.X, pos.Y))) + { + dirx = Math.Sign(v2.x - v1.x); + if (dirx != 0) + { + for (int x = pos.X; x != end.X; x += dirx) + { + blockmap[x, pos.Y].Lines.Add(line); + } + } + blockmap[end.X, end.Y].Lines.Add(line); + } + } + // Vertical straight line? + else if (pos.X == end.X) + { + // Simple loop + pos.Y = CropToRangeY(pos.Y); + end.Y = CropToRangeY(end.Y); + if (IsInRange(new Point(pos.X, pos.Y))) + { + diry = Math.Sign(v2.y - v1.y); + if (diry != 0) + { + for (int y = pos.Y; y != end.Y; y += diry) + { + blockmap[pos.X, y].Lines.Add(line); + } + } + blockmap[end.X, end.Y].Lines.Add(line); + } + } + else + { + // Add lines to this block + if (IsInRange(pos)) blockmap[pos.X, pos.Y].Lines.Add(line); + + // Moving outside the block? + if (pos != end) + { + // Calculate current block edges + float cl = pos.X * blocksize; + float cr = (pos.X + 1) * blocksize; + float ct = pos.Y * blocksize; + float cb = (pos.Y + 1) * blocksize; + + // Line directions + dirx = Math.Sign(v2.x - v1.x); + diry = Math.Sign(v2.y - v1.y); + + // Calculate offset and delta movement over x + if (dirx == 0) + { + posx = float.MaxValue; + deltax = float.MaxValue; + } + else if (dirx > 0) + { + posx = (cr - v1.x) / (v2.x - v1.x); + deltax = blocksize / (v2.x - v1.x); + } + else + { + // Calculate offset and delta movement over x + posx = (v1.x - cl) / (v1.x - v2.x); + deltax = blocksize / (v1.x - v2.x); + } + + // Calculate offset and delta movement over y + if (diry == 0) + { + posy = float.MaxValue; + deltay = float.MaxValue; + } + else if (diry > 0) + { + posy = (cb - v1.y) / (v2.y - v1.y); + deltay = blocksize / (v2.y - v1.y); + } + else + { + posy = (v1.y - ct) / (v1.y - v2.y); + deltay = blocksize / (v1.y - v2.y); + } + + // Continue while not reached the end + while (pos != end) + { + // Check in which direction to move + if (posx < posy) + { + // Move horizontally + posx += deltax; + if (pos.X != end.X) pos.X += dirx; + } + else + { + // Move vertically + posy += deltay; + if (pos.Y != end.Y) pos.Y += diry; + } + + // Add lines to this block + if (IsInRange(pos)) blockmap[pos.X, pos.Y].Lines.Add(line); + } + } + } + } + + #endregion + } +} diff --git a/Source/Core/Map/Lights.cs b/Source/Core/Map/Lights.cs new file mode 100644 index 0000000..f1222b2 --- /dev/null +++ b/Source/Core/Map/Lights.cs @@ -0,0 +1,413 @@ +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Drawing; +using System.Collections.ObjectModel; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Geometry; +using CodeImp.DoomBuilder.Rendering; +using SlimDX.Direct3D9; +using SlimDX; +#endregion + +namespace CodeImp.DoomBuilder.Map +{ + public struct Lights + { + #region ================== Constants + #endregion + + #region ================== Variables + + // Properties + public PixelColor color; + public UInt16 tag; + + #endregion + + #region ================== Constructor / Disposer + + public Lights(byte r, byte g, byte b, UInt16 tag) + { + this.color.r = r; + this.color.g = g; + this.color.b = b; + this.color.a = 255; + this.tag = tag; + } + + #endregion + + #region ================== Methods + + public int GetColor() + { + return GetFactor(this).ToInt(); + } + + public int GetColor(Lights light) + { + return GetFactor(light).ToInt(); + } + + public int GetTopColor(Sidedef s) + { + if (s.Line.IsFlagSet("67108864")) + return GetColor(s.Sector.LowerColor); + + return GetColor(s.Sector.TopColor); + } + + public int GetLowerColor(Sidedef s) + { + //if (s.Line.IsFlagSet("67108864")) + //return s.Sector.TopColor.color.ToInt(); + + return GetColor(s.Sector.LowerColor); + } + + public int UnpegUpperLight(Sidedef s) + { + int height; + Lights c = new Lights(); + int sh1; + int sh2; + Sector front; + Sector back; + float r1, g1, b1; + float r2, g2, b2; + Lights ltop, lbottom; + + if (s.Line.IsFlagSet("67108864")) + { + ltop = s.Sector.LowerColor; + lbottom = s.Sector.TopColor; + } + else + { + ltop = s.Sector.TopColor; + lbottom = s.Sector.LowerColor; + } + + ltop.color = GetFactor(ltop); + lbottom.color = GetFactor(lbottom); + + if (s.Line.IsFlagSet("2097152")) + return lbottom.color.ToInt(); + + if (s.IsFront) + { + front = s.Line.Front.Sector; + back = s.Line.Back.Sector; + } + else + { + front = s.Line.Back.Sector; + back = s.Line.Front.Sector; + } + + height = front.CeilHeight - front.FloorHeight; + + if (height == 0) + return lbottom.color.ToInt(); + + sh1 = back.CeilHeight - front.FloorHeight; + sh2 = front.CeilHeight - back.CeilHeight; + + r1 = ltop.color.r; + g1 = ltop.color.g; + b1 = ltop.color.b; + + r2 = lbottom.color.r; + g2 = lbottom.color.g; + b2 = lbottom.color.b; + + r1 = ((r1 / height) * sh1); + g1 = ((g1 / height) * sh1); + b1 = ((b1 / height) * sh1); + + r2 = ((r2 / height) * sh2); + g2 = ((g2 / height) * sh2); + b2 = ((b2 / height) * sh2); + + c.color.r = (byte)Math.Min((int)(r1 + r2), (int)255); + c.color.g = (byte)Math.Min((int)(g1 + g2), (int)255); + c.color.b = (byte)Math.Min((int)(b1 + b2), (int)255); + c.color.a = 255; + + return c.color.ToInt(); + } + + public int UnpegLowerLight(Sidedef s) + { + int height; + Lights c = new Lights(); + int sh1; + int sh2; + Sector front; + Sector back; + float r1, g1, b1; + float r2, g2, b2; + Lights ltop, lbottom; + + //if (s.Line.IsFlagSet("67108864")) + //{ + // ltop = s.Sector.LowerColor; + // lbottom = s.Sector.TopColor; + //} + //else + //{ + ltop = s.Sector.TopColor; + lbottom = s.Sector.LowerColor; + //} + + ltop.color = GetFactor(ltop); + lbottom.color = GetFactor(lbottom); + + if (s.Line.IsFlagSet("4194304")) + return ltop.color.ToInt(); + + if (s.IsFront) + { + front = s.Line.Front.Sector; + back = s.Line.Back.Sector; + } + else + { + front = s.Line.Back.Sector; + back = s.Line.Front.Sector; + } + + height = front.CeilHeight - front.FloorHeight; + + if (height == 0) + return ltop.color.ToInt(); + + sh1 = back.FloorHeight - front.FloorHeight; + sh2 = front.CeilHeight - back.FloorHeight; + + r1 = ltop.color.r; + g1 = ltop.color.g; + b1 = ltop.color.b; + + r2 = lbottom.color.r; + g2 = lbottom.color.g; + b2 = lbottom.color.b; + + r1 = ((r1 / height) * sh1); + g1 = ((g1 / height) * sh1); + b1 = ((b1 / height) * sh1); + + r2 = ((r2 / height) * sh2); + g2 = ((g2 / height) * sh2); + b2 = ((b2 / height) * sh2); + + c.color.r = (byte)Math.Min((int)(r1 + r2), (int)255); + c.color.g = (byte)Math.Min((int)(g1 + g2), (int)255); + c.color.b = (byte)Math.Min((int)(b1 + b2), (int)255); + c.color.a = 255; + + return c.color.ToInt(); + } + + private static bool PreciseCmp(float f1, float f2) + { + float precision = 0.00001f; + if (((f1 - precision) < f2) && + ((f1 + precision) > f2)) + { + return true; + } + else + { + return false; + } + } + + private static int[] GetHSV(PixelColor color) + { + int[] hsv = new int[3]; + byte r = color.r; + byte g = color.g; + byte b = color.b; + int min = r; + int max = r; + float delta = 0.0f; + float j = 0.0f; + float x = 0.0f; + float xr = 0.0f; + float xg = 0.0f; + float xb = 0.0f; + float sum = 0.0f; + + if (g < min) min = g; + if (b < min) min = b; + + if (g > max) max = g; + if (b > max) max = b; + + delta = ((float)max / 255.0f); + + if (PreciseCmp(delta, 0.0f)) + delta = 0; + else + { + j = ((delta - ((float)min / 255.0f)) / delta); + } + + if (!PreciseCmp(j, 0.0f)) + { + xr = ((float)r / 255.0f); + + if (!PreciseCmp(xr, delta)) + { + xg = ((float)g / 255.0f); + + if (!PreciseCmp(xg, delta)) + { + xb = ((float)b / 255.0f); + + if (PreciseCmp(xb, delta)) + { + sum = ((((delta - xg) / (delta - (min / 255.0f))) + 4.0f) - + ((delta - xr) / (delta - (min / 255.0f)))); + } + } + else + { + sum = ((((delta - xr) / (delta - (min / 255.0f))) + 2.0f) - + ((delta - (b / 255.0f)) / (delta - (min / 255.0f)))); + } + } + else + { + sum = (((delta - (b / 255.0f))) / (delta - (min / 255.0f))) - + ((delta - (g / 255.0f)) / (delta - (min / 255.0f))); + } + + x = (sum * 60.0f); + + if (x < 0) + x += 360.0f; + } + else + j = 0.0f; + + hsv[0] = (int)((x / 360.0f) * 255.0f); + hsv[1] = (int)(j * 255.0f); + hsv[2] = (int)(delta * 255.0f); + + return hsv; + } + + private static PixelColor GetRGB(int[] hsv) + { + float x = 0.0f; + float j = 0.0f; + float i = 0.0f; + int table = 0; + float xr = 0.0f; + float xg = 0.0f; + float xb = 0.0f; + int h = hsv[0]; + int s = hsv[1]; + int v = hsv[2]; + PixelColor color = new PixelColor(); + + j = (h / 255.0f) * 360.0f; + + if (360.0f <= j) + j -= 360.0f; + + x = (s / 255.0f); + i = (v / 255.0f); + + if (!PreciseCmp(x, 0.0f)) + { + table = (int)(j / 60.0f); + if (table < 6) + { + float t = (j / 60.0f); + switch (table) + { + case 0: + xr = i; + xg = ((1.0f - ((1.0f - (t - (float)table)) * x)) * i); + xb = ((1.0f - x) * i); + break; + case 1: + xr = ((1.0f - (x * (t - (float)table))) * i); + xg = i; + xb = ((1.0f - x) * i); + break; + case 2: + xr = ((1.0f - x) * i); + xg = i; + xb = ((1.0f - ((1.0f - (t - (float)table)) * x)) * i); + break; + case 3: + xr = ((1.0f - x) * i); + xg = ((1.0f - (x * (t - (float)table))) * i); + xb = i; + break; + case 4: + xr = ((1.0f - ((1.0f - (t - (float)table)) * x)) * i); + xg = ((1.0f - x) * i); + xb = i; + break; + case 5: + xr = i; + xg = ((1.0f - x) * i); + xb = ((1.0f - (x * (t - (float)table))) * i); + break; + } + } + } + else + xr = xg = xb = i; + + color.r = (byte)(xr * 255.0f); + color.g = (byte)(xg * 255.0f); + color.b = (byte)(xb * 255.0f); + color.a = 255; + + return color; + } + + private static PixelColor GetFactor(Lights light) + { + float factor = 1.0f + (General.Settings.LightIntensity / 10.0f); + int[] hsv = GetHSV(light.color); + hsv[2] = Math.Min((int)((float)hsv[2] * factor), 255); + return GetRGB(hsv); + } + + public void SetIntensity(float value) + { + float factor = 1.0f + value; + int[] hsv = GetHSV(this.color); + hsv[2] = Math.Min((int)((float)hsv[2] * factor), 255); + this.color = GetRGB(hsv); + } + + #endregion + + } +} diff --git a/Source/Core/Map/Linedef.cs b/Source/Core/Map/Linedef.cs new file mode 100644 index 0000000..a1ca1fb --- /dev/null +++ b/Source/Core/Map/Linedef.cs @@ -0,0 +1,1084 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using CodeImp.DoomBuilder.Config; +using CodeImp.DoomBuilder.Geometry; +using CodeImp.DoomBuilder.Rendering; +using SlimDX.Direct3D9; +using System.Drawing; +using CodeImp.DoomBuilder.IO; + +#endregion + +namespace CodeImp.DoomBuilder.Map +{ + public sealed class Linedef : SelectableElement + { + #region ================== Constants + + public const float SIDE_POINT_DISTANCE = 0.01f; + public const int NUM_ARGS = 5; + + #endregion + + #region ================== Variables + + // Map + private MapSet map; + + // List items + private LinkedListNode startvertexlistitem; + private LinkedListNode endvertexlistitem; + private LinkedListNode selecteditem; + + // Vertices + private Vertex start; + private Vertex end; + + // Sidedefs + private Sidedef front; + private Sidedef back; + + // Cache + private bool updateneeded; + private float lengthsq; + private float lengthsqinv; + private float length; + private float lengthinv; + private float angle; + private RectangleF rect; + private bool blocksoundflag; + private bool impassableflag; + private bool invisibleflag; //villsa + private bool secretflag; // villsa + private bool monsterblockflag; // villsa + private bool tagonlyflag; // villsa + + // Properties + private Dictionary flags; + private int action; + private int activate; + private int switchmask; // villsa 9/12/11 + private int tag; + private int[] args; + private bool frontinterior; // for drawing only + + // Clone + private int serializedindex; + + #endregion + + #region ================== Properties + + public MapSet Map { get { return map; } } + public Vertex Start { get { return start; } } + public Vertex End { get { return end; } } + public Sidedef Front { get { return front; } } + public Sidedef Back { get { return back; } } + public Line2D Line { get { return new Line2D(start.Position, end.Position); } } + internal Dictionary Flags { get { return flags; } } + public int Action { get { return action; } set { BeforePropsChange(); action = value; } } + public int Activate { get { return activate; } set { BeforePropsChange(); activate = value; } } + public int SwitchMask { get { return switchmask; } set { BeforePropsChange(); switchmask = value; } } + public int Tag { get { return tag; } set { BeforePropsChange(); tag = value; if ((tag < General.Map.FormatInterface.MinTag) || (tag > General.Map.FormatInterface.MaxTag)) throw new ArgumentOutOfRangeException("Tag", "Invalid tag number"); } } + public float LengthSq { get { return lengthsq; } } + public float Length { get { return length; } } + public float LengthInv { get { return lengthinv; } } + public float Angle { get { return angle; } } + public int AngleDeg { get { return (int)(angle * Angle2D.PIDEG); } } + public RectangleF Rect { get { return rect; } } + public int[] Args { get { return args; } } + internal int SerializedIndex { get { return serializedindex; } set { serializedindex = value; } } + internal bool FrontInterior { get { return frontinterior; } set { frontinterior = value; } } + internal bool ImpassableFlag { get { return impassableflag; } } + internal bool BlockSoundFlag { get { return blocksoundflag; } } + internal bool InvisibleFlag { get { return invisibleflag; } } // villsa + internal bool MonsterBlockFlag { get { return monsterblockflag; } } // villsa + internal bool SecretFlag { get { return secretflag; } } // villsa + internal bool TagonlyFlag { get { return tagonlyflag; } set { tagonlyflag = value; } } // villsa + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + internal Linedef(MapSet map, int listindex, Vertex start, Vertex end) + { + // Initialize + this.map = map; + this.listindex = listindex; + this.updateneeded = true; + this.args = new int[NUM_ARGS]; + this.flags = new Dictionary(); + + // Attach to vertices + this.start = start; + this.startvertexlistitem = start.AttachLinedefP(this); + this.end = end; + this.endvertexlistitem = end.AttachLinedefP(this); + + if (map == General.Map.Map) + General.Map.UndoRedo.RecAddLinedef(this); + + // We have no destructor + GC.SuppressFinalize(this); + } + + // Disposer + public override void Dispose() + { + // Not already disposed? + if (!isdisposed) + { + // Already set isdisposed so that changes can be prohibited + isdisposed = true; + + // Dispose sidedefs + if ((front != null) && map.AutoRemove) front.Dispose(); else AttachFrontP(null); + if ((back != null) && map.AutoRemove) back.Dispose(); else AttachBackP(null); + + if (map == General.Map.Map) + General.Map.UndoRedo.RecRemLinedef(this); + + // Remove from main list + map.RemoveLinedef(listindex); + + // Detach from vertices + if (startvertexlistitem != null) start.DetachLinedefP(startvertexlistitem); + startvertexlistitem = null; + start = null; + if (endvertexlistitem != null) end.DetachLinedefP(endvertexlistitem); + endvertexlistitem = null; + end = null; + + // Clean up + start = null; + end = null; + front = null; + back = null; + map = null; + + // Clean up base + base.Dispose(); + } + } + + #endregion + + #region ================== Management + + // Call this before changing properties + protected override void BeforePropsChange() + { + if (map == General.Map.Map) + General.Map.UndoRedo.RecPrpLinedef(this); + } + + // Serialize / deserialize (passive: doesn't record) + internal void ReadWrite(IReadWriteStream s) + { + if (!s.IsWriting) + { + BeforePropsChange(); + updateneeded = true; + } + + base.ReadWrite(s); + + if (s.IsWriting) + { + s.wInt(flags.Count); + + foreach (KeyValuePair f in flags) + { + s.wString(f.Key); + s.wBool(f.Value); + } + } + else + { + int c; s.rInt(out c); + + flags = new Dictionary(c); + for (int i = 0; i < c; i++) + { + string t; s.rString(out t); + bool b; s.rBool(out b); + flags.Add(t, b); + } + } + + s.rwInt(ref action); + s.rwInt(ref activate); + s.rwInt(ref tag); + for (int i = 0; i < NUM_ARGS; i++) s.rwInt(ref args[i]); + } + + // This sets new start vertex + public void SetStartVertex(Vertex v) + { + if (map == General.Map.Map) + General.Map.UndoRedo.RecRefLinedefStart(this); + + // Change start + if (startvertexlistitem != null) start.DetachLinedefP(startvertexlistitem); + startvertexlistitem = null; + start = v; + if (start != null) startvertexlistitem = start.AttachLinedefP(this); + this.updateneeded = true; + } + + // This sets new end vertex + public void SetEndVertex(Vertex v) + { + if (map == General.Map.Map) + General.Map.UndoRedo.RecRefLinedefEnd(this); + + // Change end + if (endvertexlistitem != null) end.DetachLinedefP(endvertexlistitem); + endvertexlistitem = null; + end = v; + if (end != null) endvertexlistitem = end.AttachLinedefP(this); + this.updateneeded = true; + } + + // This detaches a vertex + internal void DetachVertexP(Vertex v) + { + if (v == start) + { + if (startvertexlistitem != null) start.DetachLinedefP(startvertexlistitem); + startvertexlistitem = null; + start = null; + } + else if (v == end) + { + if (endvertexlistitem != null) end.DetachLinedefP(endvertexlistitem); + endvertexlistitem = null; + end = null; + } + else + throw new Exception("Specified Vertex is not attached to this Linedef."); + } + + // This copies all properties to another line + new public void CopyPropertiesTo(Linedef l) + { + l.BeforePropsChange(); + + // Copy properties + l.action = action; + l.args = (int[])args.Clone(); + l.flags = new Dictionary(flags); + l.tag = tag; + l.updateneeded = true; + l.activate = activate; + l.impassableflag = impassableflag; + l.blocksoundflag = blocksoundflag; + l.invisibleflag = invisibleflag; // villsa + l.switchmask = switchmask; + l.secretflag = secretflag; + l.monsterblockflag = monsterblockflag; + l.tagonlyflag = tagonlyflag; + base.CopyPropertiesTo(l); + } + + // This attaches a sidedef on the front + internal void AttachFront(Sidedef s) + { + if (map == General.Map.Map) + General.Map.UndoRedo.RecRefLinedefFront(this); + + // Attach and recalculate + AttachFrontP(s); + } + + // Passive version, does not record the change + internal void AttachFrontP(Sidedef s) + { + // Attach and recalculate + front = s; + if (front != null) front.SetLinedefP(this); + updateneeded = true; + } + + // This attaches a sidedef on the back + internal void AttachBack(Sidedef s) + { + if (map == General.Map.Map) + General.Map.UndoRedo.RecRefLinedefBack(this); + + // Attach and recalculate + AttachBackP(s); + } + + // Passive version, does not record the change + internal void AttachBackP(Sidedef s) + { + // Attach and recalculate + back = s; + if (back != null) back.SetLinedefP(this); + updateneeded = true; + } + + // This detaches a sidedef from the front + internal void DetachSidedefP(Sidedef s) + { + // Sidedef is on the front? + if (front == s) + { + // Remove sidedef reference + if (front != null) front.SetLinedefP(null); + front = null; + updateneeded = true; + } + // Sidedef is on the back? + else if (back == s) + { + // Remove sidedef reference + if (back != null) back.SetLinedefP(null); + back = null; + updateneeded = true; + } + //else throw new Exception("Specified Sidedef is not attached to this Linedef."); + } + + // This updates the line when changes have been made + public void UpdateCache() + { + this.TagonlyFlag = ((this.Tag != 0) && ((this.Action & 511) == 0)); + + // Update if needed + if (updateneeded) + { + // Delta vector + Vector2D delta = end.Position - start.Position; + + // Recalculate values + lengthsq = delta.GetLengthSq(); + length = (float)Math.Sqrt(lengthsq); + if (length > 0f) lengthinv = 1f / length; else lengthinv = 1f / 0.0000000001f; + if (lengthsq > 0f) lengthsqinv = 1f / lengthsq; else lengthsqinv = 1f / 0.0000000001f; + angle = delta.GetAngle(); + float l = Math.Min(start.Position.x, end.Position.x); + float t = Math.Min(start.Position.y, end.Position.y); + float r = Math.Max(start.Position.x, end.Position.x); + float b = Math.Max(start.Position.y, end.Position.y); + rect = new RectangleF(l, t, r - l, b - t); + + // Cached flags + blocksoundflag = IsFlagSet(General.Map.Config.SoundLinedefFlag); + impassableflag = IsFlagSet(General.Map.Config.ImpassableFlag); + invisibleflag = IsFlagSet(General.Map.Config.InvisibleFlag); // villsa + monsterblockflag = IsFlagSet(General.Map.Config.MonsterblockFlag); + secretflag = IsFlagSet(General.Map.Config.SecretFlag); + + // Updated + updateneeded = false; + } + } + + // This flags the line needs an update because it moved + public void NeedUpdate() + { + // Update this line + updateneeded = true; + + // Update sectors as well + if (front != null) front.Sector.UpdateNeeded = true; + if (back != null) back.Sector.UpdateNeeded = true; + } + + // This translates the flags and activations into UDMF fields + internal void TranslateToUDMF() + { + // First make a single integer with all bits from activation and flags + int bits = activate; + int flagbit = 0; + foreach (KeyValuePair f in flags) + if (int.TryParse(f.Key, out flagbit) && f.Value) bits |= flagbit; + + bits |= switchmask; + + // Now make the new flags + flags.Clear(); + foreach (FlagTranslation f in General.Map.Config.LinedefFlagsTranslation) + { + // Flag found in bits? + if ((bits & f.Flag) == f.Flag) + { + // Add fields and remove bits + bits &= ~f.Flag; + for (int i = 0; i < f.Fields.Count; i++) + flags[f.Fields[i]] = f.FieldValues[i]; + } + else + { + // Add fields with inverted value + for (int i = 0; i < f.Fields.Count; i++) + flags[f.Fields[i]] = !f.FieldValues[i]; + } + } + } + + // This translates UDMF fields back into the normal flags and activations + internal void TranslateFromUDMF() + { + // Make copy of the flags + Dictionary oldfields = new Dictionary(flags); + + // Make the flags + flags.Clear(); + foreach (KeyValuePair f in General.Map.Config.LinedefFlags) + { + // Flag must be numeric + int flagbit = 0; + if (int.TryParse(f.Key, out flagbit)) + { + foreach (FlagTranslation ft in General.Map.Config.LinedefFlagsTranslation) + { + if (ft.Flag == flagbit) + { + // Only set this flag when the fields match + bool fieldsmatch = true; + for (int i = 0; i < ft.Fields.Count; i++) + { + if (!oldfields.ContainsKey(ft.Fields[i]) || (oldfields[ft.Fields[i]] != ft.FieldValues[i])) + { + fieldsmatch = false; + break; + } + } + + // Field match? Then add the flag. + if (fieldsmatch) + { + flags.Add(f.Key, true); + break; + } + } + } + } + } + + // Make the activation + foreach (LinedefActivateInfo a in General.Map.Config.LinedefActivates) + { + bool foundactivation = false; + foreach (FlagTranslation ft in General.Map.Config.LinedefFlagsTranslation) + { + if (ft.Flag == a.Index) + { + // Only set this activation when the fields match + bool fieldsmatch = true; + for (int i = 0; i < ft.Fields.Count; i++) + { + if (!oldfields.ContainsKey(ft.Fields[i]) || (oldfields[ft.Fields[i]] != ft.FieldValues[i])) + { + fieldsmatch = false; + break; + } + } + + // Field match? Then add the flag. + if (fieldsmatch) + { + activate = a.Index; + foundactivation = true; + break; + } + } + } + if (foundactivation) break; + } + } + + // Selected + protected override void DoSelect() + { + base.DoSelect(); + selecteditem = map.SelectedLinedefs.AddLast(this); + } + + // Deselect + protected override void DoUnselect() + { + base.DoUnselect(); + if (selecteditem.List != null) selecteditem.List.Remove(selecteditem); + selecteditem = null; + } + + #endregion + + #region ================== Methods + + // This checks and returns a flag without creating it + public bool IsFlagSet(string flagname) + { + if (flags.ContainsKey(flagname)) + return flags[flagname]; + else + return false; + } + + // This sets a flag + public void SetFlag(string flagname, bool value) + { + if (!flags.ContainsKey(flagname) || (IsFlagSet(flagname) != value)) + { + BeforePropsChange(); + + flags[flagname] = value; + + // Cached flags + if (flagname == General.Map.Config.SoundLinedefFlag) blocksoundflag = value; + if (flagname == General.Map.Config.ImpassableFlag) impassableflag = value; + if (flagname == General.Map.Config.InvisibleFlag) invisibleflag = value; // villsa + if (flagname == General.Map.Config.MonsterblockFlag) monsterblockflag = value; // villsa + if (flagname == General.Map.Config.SecretFlag) secretflag = value; // villsa + } + } + + // This returns a copy of the flags dictionary + public Dictionary GetFlags() + { + return new Dictionary(flags); + } + + // This clears all flags + public void ClearFlags() + { + BeforePropsChange(); + flags.Clear(); + blocksoundflag = false; + impassableflag = false; + invisibleflag = false; // villsa + monsterblockflag = false; + secretflag = false; + } + + // This flips the linedef's vertex attachments + public void FlipVertices() + { + // make sure the start/end vertices are not automatically + // deleted if they do not belong to any other line + General.Map.Map.AutoRemove = false; + + // Flip vertices + Vertex oldstart = start; + Vertex oldend = end; + SetStartVertex(oldend); + SetEndVertex(oldstart); + + General.Map.Map.AutoRemove = true; + + // For drawing, the interior now lies on the other side + frontinterior = !frontinterior; + + // Update required (angle changed) + NeedUpdate(); + General.Map.IsChanged = true; + } + + // This flips the sidedefs + public void FlipSidedefs() + { + // Flip sidedefs + Sidedef oldfront = front; + Sidedef oldback = back; + AttachFront(oldback); + AttachBack(oldfront); + + General.Map.IsChanged = true; + } + + // This returns a point for testing on one side + public Vector2D GetSidePoint(bool front) + { + Vector2D n = new Vector2D(); + n.x = (end.Position.x - start.Position.x) * lengthinv * SIDE_POINT_DISTANCE; + n.y = (end.Position.y - start.Position.y) * lengthinv * SIDE_POINT_DISTANCE; + + if (front) + { + n.x = -n.x; + n.y = -n.y; + } + + Vector2D p = new Vector2D(); + p.x = start.Position.x + (end.Position.x - start.Position.x) * 0.5f - n.y; + p.y = start.Position.y + (end.Position.y - start.Position.y) * 0.5f + n.x; + + return p; + } + + // This returns a point in the middle of the line + public Vector2D GetCenterPoint() + { + return start.Position + (end.Position - start.Position) * 0.5f; + } + + // This applies single/double sided flags + public void ApplySidedFlags() + { + // Doublesided? + if ((front != null) && (back != null)) + { + // Apply or remove flags for doublesided line + SetFlag(General.Map.Config.SingleSidedFlag, false); + SetFlag(General.Map.Config.DoubleSidedFlag, true); + } + else + { + // Apply or remove flags for singlesided line + SetFlag(General.Map.Config.SingleSidedFlag, true); + SetFlag(General.Map.Config.DoubleSidedFlag, false); + } + + General.Map.IsChanged = true; + } + + // This returns all points at which the line intersects with the grid + public List GetGridIntersections() + { + List coords = new List(); + Vector2D v = new Vector2D(); + float gx, gy, minx, maxx, miny, maxy; + bool reversex, reversey; + + if (start.Position.x > end.Position.x) + { + minx = end.Position.x; + maxx = start.Position.x; + reversex = true; + } + else + { + minx = start.Position.x; + maxx = end.Position.x; + reversex = false; + } + + if (start.Position.y > end.Position.y) + { + miny = end.Position.y; + maxy = start.Position.y; + reversey = true; + } + else + { + miny = start.Position.y; + maxy = end.Position.y; + reversey = false; + } + + // Go for all vertical grid lines in between line start and end + gx = General.Map.Grid.GetHigher(minx); + if (gx < maxx) + { + for (; gx < maxx; gx += General.Map.Grid.GridSizeF) + { + // Add intersection point at this x coordinate + float u = (gx - minx) / (maxx - minx); + if (reversex) u = 1.0f - u; + v.x = gx; + v.y = start.Position.y + (end.Position.y - start.Position.y) * u; + coords.Add(v); + } + } + + // Go for all horizontal grid lines in between line start and end + gy = General.Map.Grid.GetHigher(miny); + if (gy < maxy) + { + for (; gy < maxy; gy += General.Map.Grid.GridSizeF) + { + // Add intersection point at this y coordinate + float u = (gy - miny) / (maxy - miny); + if (reversey) u = 1.0f - u; + v.x = start.Position.x + (end.Position.x - start.Position.x) * u; + v.y = gy; + coords.Add(v); + } + } + + // Profit + return coords; + } + + // This returns the closest coordinates ON the line + public Vector2D NearestOnLine(Vector2D pos) + { + float u = Line2D.GetNearestOnLine(start.Position, end.Position, pos); + if (u < 0f) u = 0f; else if (u > 1f) u = 1f; + return Line2D.GetCoordinatesAt(start.Position, end.Position, u); + } + + // This returns the shortest distance from given coordinates to line + public float SafeDistanceToSq(Vector2D p, bool bounded) + { + Vector2D v1 = start.Position; + Vector2D v2 = end.Position; + + // Calculate intersection offset + float u = ((p.x - v1.x) * (v2.x - v1.x) + (p.y - v1.y) * (v2.y - v1.y)) * lengthsqinv; + + // Limit intersection offset to the line + if (bounded) if (u < lengthinv) u = lengthinv; else if (u > (1f - lengthinv)) u = 1f - lengthinv; + + // Calculate intersection point + Vector2D i = v1 + u * (v2 - v1); + + // Return distance between intersection and point + // which is the shortest distance to the line + float ldx = p.x - i.x; + float ldy = p.y - i.y; + return ldx * ldx + ldy * ldy; + } + + // This returns the shortest distance from given coordinates to line + public float SafeDistanceTo(Vector2D p, bool bounded) + { + return (float)Math.Sqrt(SafeDistanceToSq(p, bounded)); + } + + // This returns the shortest distance from given coordinates to line + public float DistanceToSq(Vector2D p, bool bounded) + { + Vector2D v1 = start.Position; + Vector2D v2 = end.Position; + + // Calculate intersection offset + float u = ((p.x - v1.x) * (v2.x - v1.x) + (p.y - v1.y) * (v2.y - v1.y)) * lengthsqinv; + + // Limit intersection offset to the line + if (bounded) if (u < 0f) u = 0f; else if (u > 1f) u = 1f; + + // Calculate intersection point + Vector2D i = v1 + u * (v2 - v1); + + // Return distance between intersection and point + // which is the shortest distance to the line + float ldx = p.x - i.x; + float ldy = p.y - i.y; + return ldx * ldx + ldy * ldy; + } + + // This returns the shortest distance from given coordinates to line + public float DistanceTo(Vector2D p, bool bounded) + { + return (float)Math.Sqrt(DistanceToSq(p, bounded)); + } + + // This tests on which side of the line the given coordinates are + // returns < 0 for front (right) side, > 0 for back (left) side and 0 if on the line + public float SideOfLine(Vector2D p) + { + Vector2D v1 = start.Position; + Vector2D v2 = end.Position; + + // Calculate and return side information + return (p.y - v1.y) * (v2.x - v1.x) - (p.x - v1.x) * (v2.y - v1.y); + } + + // This splits this line by vertex v + // Returns the new line resulting from the split, or null when it failed + public Linedef Split(Vertex v) + { + Linedef nl; + Sidedef nsd; + + // Copy linedef and change vertices + nl = map.CreateLinedef(v, end); + if (nl == null) return null; + CopyPropertiesTo(nl); + SetEndVertex(v); + nl.Selected = this.Selected; + nl.marked = this.marked; + + // Copy front sidedef if exists + if (front != null) + { + nsd = map.CreateSidedef(nl, true, front.Sector); + if (nsd == null) return null; + front.CopyPropertiesTo(nsd); + nsd.Marked = front.Marked; + + // Make texture offset adjustments + nsd.OffsetX += (int)Vector2D.Distance(this.start.Position, this.end.Position); + } + + // Copy back sidedef if exists + if (back != null) + { + nsd = map.CreateSidedef(nl, false, back.Sector); + if (nsd == null) return null; + back.CopyPropertiesTo(nsd); + nsd.Marked = back.Marked; + + // Make texture offset adjustments + back.OffsetX += (int)Vector2D.Distance(nl.start.Position, nl.end.Position); + } + + // Return result + General.Map.IsChanged = true; + return nl; + } + + // This joins the line with another line + // This line will be disposed + // Returns false when the operation could not be completed + public bool Join(Linedef other) + { + Sector l1fs, l1bs, l2fs, l2bs; + bool l1was2s, l2was2s; + + // Check which lines were 2 sided + l1was2s = ((other.Front != null) && (other.Back != null)); + l2was2s = ((this.Front != null) && (this.Back != null)); + + // Get sector references + if (other.front != null) l1fs = other.front.Sector; else l1fs = null; + if (other.back != null) l1bs = other.back.Sector; else l1bs = null; + if (this.front != null) l2fs = this.front.Sector; else l2fs = null; + if (this.back != null) l2bs = this.back.Sector; else l2bs = null; + + // This line has no sidedefs? + if ((l2fs == null) && (l2bs == null)) + { + // We have no sidedefs, so we have no influence + // Nothing to change on the other line + } + // Other line has no sidedefs? + else if ((l1fs == null) && (l1bs == null)) + { + // The other has no sidedefs, so it has no influence + // Copy my sidedefs to the other + if (this.Start == other.Start) + { + if (!JoinChangeSidedefs(other, true, front)) return false; + if (!JoinChangeSidedefs(other, false, back)) return false; + } + else + { + if (!JoinChangeSidedefs(other, false, front)) return false; + if (!JoinChangeSidedefs(other, true, back)) return false; + } + + // Copy my properties to the other + this.CopyPropertiesTo(other); + } + else + { + // Compare front sectors + if ((l1fs != null) && (l1fs == l2fs)) + { + // Copy textures + if (other.front != null) other.front.AddTexturesTo(this.back); + if (this.front != null) this.front.AddTexturesTo(other.back); + + // Change sidedefs + if (!JoinChangeSidedefs(other, true, back)) return false; + } + // Compare back sectors + else if ((l1bs != null) && (l1bs == l2bs)) + { + // Copy textures + if (other.back != null) other.back.AddTexturesTo(this.front); + if (this.back != null) this.back.AddTexturesTo(other.front); + + // Change sidedefs + if (!JoinChangeSidedefs(other, false, front)) return false; + } + // Compare front and back + else if ((l1fs != null) && (l1fs == l2bs)) + { + // Copy textures + if (other.front != null) other.front.AddTexturesTo(this.front); + if (this.back != null) this.back.AddTexturesTo(other.back); + + // Change sidedefs + if (!JoinChangeSidedefs(other, true, front)) return false; + } + // Compare back and front + else if ((l1bs != null) && (l1bs == l2fs)) + { + // Copy textures + if (other.back != null) other.back.AddTexturesTo(this.back); + if (this.front != null) this.front.AddTexturesTo(other.front); + + // Change sidedefs + if (!JoinChangeSidedefs(other, false, back)) return false; + } + else + { + // Other line single sided? + if (other.back == null) + { + // This line with its back to the other? + if (this.start == other.end) + { + // Copy textures + if (other.back != null) other.back.AddTexturesTo(this.front); + if (this.back != null) this.back.AddTexturesTo(other.front); + + // Change sidedefs + if (!JoinChangeSidedefs(other, false, front)) return false; + } + else + { + // Copy textures + if (other.back != null) other.back.AddTexturesTo(this.back); + if (this.front != null) this.front.AddTexturesTo(other.front); + + // Change sidedefs + if (!JoinChangeSidedefs(other, false, back)) return false; + } + } + // This line single sided? + else if (this.back == null) + { + // Other line with its back to this? + if (other.start == this.end) + { + // Copy textures + if (other.back != null) other.back.AddTexturesTo(this.front); + if (this.back != null) this.back.AddTexturesTo(other.front); + + // Change sidedefs + if (!JoinChangeSidedefs(other, false, front)) return false; + } + else + { + // Copy textures + if (other.front != null) other.front.AddTexturesTo(this.front); + if (this.back != null) this.back.AddTexturesTo(other.back); + + // Change sidedefs + if (!JoinChangeSidedefs(other, true, front)) return false; + } + } + else + { + // This line with its back to the other? + if (this.start == other.end) + { + // Copy textures + if (other.back != null) other.back.AddTexturesTo(this.front); + if (this.back != null) this.back.AddTexturesTo(other.front); + + // Change sidedefs + if (!JoinChangeSidedefs(other, false, front)) return false; + } + else + { + // Copy textures + if (other.back != null) other.back.AddTexturesTo(this.back); + if (this.front != null) this.front.AddTexturesTo(other.front); + + // Change sidedefs + if (!JoinChangeSidedefs(other, false, back)) return false; + } + } + } + + // Apply single/double sided flags if the double-sided-ness changed + if ((!l1was2s && ((other.Front != null) && (other.Back != null))) || + (l1was2s && ((other.Front == null) || (other.Back == null)))) + other.ApplySidedFlags(); + + // Remove unneeded textures + if (other.front != null) other.front.RemoveUnneededTextures(!(l1was2s && l2was2s)); + if (other.back != null) other.back.RemoveUnneededTextures(!(l1was2s && l2was2s)); + } + + // If either of the two lines was selected, keep the other selected + if (this.Selected) other.Selected = true; + if (this.marked) other.marked = true; + + // I got killed by the other. + this.Dispose(); + General.Map.IsChanged = true; + return true; + } + + // This changes sidedefs (used for joining lines) + // target: The linedef on which to remove or create a new sidedef + // front: Side on which to remove or create the sidedef (true for front side) + // newside: The side from which to copy the properties to the new sidedef. + // If this is null, no sidedef will be created (only removed) + // Returns false when the operation could not be completed. + private bool JoinChangeSidedefs(Linedef target, bool front, Sidedef newside) + { + Sidedef sd; + + // Change sidedefs + if (front) + { + if (target.front != null) target.front.Dispose(); + } + else + { + if (target.back != null) target.back.Dispose(); + } + + if (newside != null) + { + sd = map.CreateSidedef(target, front, newside.Sector); + if (sd == null) return false; + newside.CopyPropertiesTo(sd); + sd.Marked = newside.Marked; + } + + return true; + } + + // String representation + public override string ToString() + { + return "Linedef " + listindex; + } + + #endregion + + #region ================== Changes + + // This updates all properties + public void Update(Dictionary flags, int activate, int tag, int action, int mask, int[] args) + { + BeforePropsChange(); + + // Apply changes + this.flags = new Dictionary(flags); + this.tag = tag; + this.activate = activate; + this.action = action; + this.switchmask = mask; // villsa 9/12/11 + this.args = new int[NUM_ARGS]; + args.CopyTo(this.args, 0); + this.updateneeded = true; + } + + #endregion + } +} diff --git a/Source/Core/Map/Macro.cs b/Source/Core/Map/Macro.cs new file mode 100644 index 0000000..b60205f --- /dev/null +++ b/Source/Core/Map/Macro.cs @@ -0,0 +1,133 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using CodeImp.DoomBuilder.Config; +using CodeImp.DoomBuilder.Geometry; +using CodeImp.DoomBuilder.Rendering; +using SlimDX.Direct3D9; +using System.Drawing; +using CodeImp.DoomBuilder.IO; + +#endregion + +namespace CodeImp.DoomBuilder.Map +{ + public class Macro + { + #region ================== Constants + + #endregion + + #region ================== Variables + + private MacroData[] data; + private int count; + + #endregion + + #region ================== Properties + + public MacroData[] MacroData { get { return data; } } + public int Count { get { return count; } } + + #endregion + + #region ================== Constructor / Disposer + + public Macro(int count) + { + int i; + + data = new MacroData[count]; + + for (i = 0; i < count; i++) + { + data[i].batch = 0; + data[i].tag = 0; + data[i].type = 0; + data[i].indexref = -1; + } + + this.count = count; + + GC.SuppressFinalize(this); + } + + #endregion + + #region ================== Methods + + public void SetDataFromTreeNode(TreeView tree) + { + int i = 0; + int batchid = 10; + + count = tree.GetNodeCount(true); + Array.Resize(ref data, count); + + foreach (TreeNode n in tree.Nodes) + { + foreach (TreeNode nn in n.Nodes) + { + data[i] = (MacroData)nn.Tag; + data[i].batch = batchid; + i++; + } + batchid += 10; + } + + Array.Resize(ref data, i); + count = i; + } + + public void SetNodeFromData(int macroid, TreeView tree) + { + int batch = -1; + TreeNode n = tree.TopNode; + + foreach (MacroData m in data) + { + if (batch != m.batch) + { + batch = m.batch; + n = tree.Nodes.Add("Batch " + m.batch); + } + + TreeNode nn = n.Nodes.Add("Action"); + nn.Text = m.SetName(); + nn.Tag = m; + } + tree.ExpandAll(); + } + + public void Set(int index, int type, int batch, int tag) + { + data[index].type = type; + data[index].tag = tag; + data[index].batch = batch; + } + + #endregion + } +} diff --git a/Source/Core/Map/MacroData.cs b/Source/Core/Map/MacroData.cs new file mode 100644 index 0000000..c5da243 --- /dev/null +++ b/Source/Core/Map/MacroData.cs @@ -0,0 +1,71 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Reflection; +using System.Drawing; +using SlimDX.Direct3D9; +using SlimDX; + +#endregion + +namespace CodeImp.DoomBuilder.Map +{ + public struct MacroData + { + #region ================== Constants + + #endregion + + #region ================== Variables + + public int type; + public int tag; + public int batch; + public int indexref; + + #endregion + + #region ================== Constructors + + // Constructor + public MacroData(int type, int tag, int batch, int index) + { + // Initialize + this.type = type; + this.tag = tag; + this.batch = batch; + this.indexref = index; + } + + #endregion + + #region ================== Methods + + public string SetName() + { + return "(" + type + ") " + General.Map.Config.LinedefActions[type].Name + " : " + tag; + } + + #endregion + } +} diff --git a/Source/Core/Map/MapElement.cs b/Source/Core/Map/MapElement.cs new file mode 100644 index 0000000..35e5dcb --- /dev/null +++ b/Source/Core/Map/MapElement.cs @@ -0,0 +1,132 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using CodeImp.DoomBuilder.Geometry; +using CodeImp.DoomBuilder.Rendering; +using SlimDX.Direct3D9; +using System.Drawing; +using CodeImp.DoomBuilder.IO; + +#endregion + +namespace CodeImp.DoomBuilder.Map +{ + public abstract class MapElement : IDisposable + { + #region ================== Constants + + #endregion + + #region ================== Variables + + // List index + protected int listindex; + + // Univeral fields + private UniFields fields; + + // Marking + protected bool marked; + + // Disposing + protected bool isdisposed = false; + + #endregion + + #region ================== Properties + + public int Index { get { return listindex; } internal set { listindex = value; } } + public UniFields Fields { get { return fields; } } + public bool Marked { get { return marked; } set { marked = value; } } + public bool IsDisposed { get { return isdisposed; } } + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + internal MapElement() + { + // Initialize + fields = new UniFields(this); + } + + // Disposer + public virtual void Dispose() + { + // Clean up + fields.Owner = null; + fields = null; + + // Done + isdisposed = true; + } + + #endregion + + #region ================== Methods + + // Serialize / deserialize + internal void ReadWrite(IReadWriteStream s) + { + int c = fields.Count; + s.rwInt(ref c); + + if (s.IsWriting) + { + foreach (KeyValuePair f in fields) + { + s.wString(f.Key); + f.Value.ReadWrite(s); + } + } + else + { + fields = new UniFields(this, c); + for (int i = 0; i < c; i++) + { + string t; s.rString(out t); + UniValue v = new UniValue(); v.ReadWrite(s); + fields.Add(t, v); + } + } + } + + // This copies properties to any other element + public void CopyPropertiesTo(MapElement element) + { + element.fields = new UniFields(this, this.fields); + } + + // This must implement the call to the undo system to record the change of properties + protected abstract void BeforePropsChange(); + + // This is called before the custom fields change + internal void BeforeFieldsChange() + { + BeforePropsChange(); + } + + #endregion + } +} diff --git a/Source/Core/Map/MapElementCollection.cs b/Source/Core/Map/MapElementCollection.cs new file mode 100644 index 0000000..665064e --- /dev/null +++ b/Source/Core/Map/MapElementCollection.cs @@ -0,0 +1,82 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Drawing; +using SlimDX.Direct3D9; +using SlimDX; +using System.Collections; +using System.Collections.Generic; + +#endregion + +namespace CodeImp.DoomBuilder.Map +{ + public sealed class MapElementCollection : ICollection where T : MapElement + { + // Members + private int numitems; + private T[] array; + + // Constructor + public MapElementCollection(ref T[] array, int numitems) + { + this.numitems = numitems; + this.array = array; + } + + public void Add(T item) + { + throw new NotSupportedException(); + } + + public void Clear() + { + throw new NotSupportedException(); + } + + public bool Contains(T item) + { + throw new NotSupportedException(); + } + + public void CopyTo(T[] array, int arrayIndex) + { + this.array.CopyTo(array, arrayIndex); + } + + public int Count { get { return numitems; } } + + public bool IsReadOnly { get { return true; } } + + public bool Remove(T item) + { + throw new NotSupportedException(); + } + + public IEnumerator GetEnumerator() + { + throw new NotSupportedException("This array is locked for modification and cannot currently be enumerated."); + } + + IEnumerator IEnumerable.GetEnumerator() + { + throw new NotSupportedException("This array is locked for modification and cannot currently be enumerated."); + } + } +} diff --git a/Source/Core/Map/MapOptions.cs b/Source/Core/Map/MapOptions.cs new file mode 100644 index 0000000..a147707 --- /dev/null +++ b/Source/Core/Map/MapOptions.cs @@ -0,0 +1,321 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Reflection; +using System.Text; +using CodeImp.DoomBuilder.IO; +using System.IO; +using CodeImp.DoomBuilder.Data; +using CodeImp.DoomBuilder.Config; +using CodeImp.DoomBuilder.Plugins; + +#endregion + +namespace CodeImp.DoomBuilder.Map +{ + public sealed class MapOptions + { + #region ================== Constants + + #endregion + + #region ================== Variables + + // Map configuration + private Configuration mapconfig; + + // Game configuration + private string configfile; + + // Map header name + private string currentname; + private string previousname; // When zero length string, map has not renamed + + // Strict pathes loading? + private bool strictpatches; + + // Additional resources + private DataLocationList resources; + + // Script files opened + private List scriptfiles; + + #endregion + + #region ================== Properties + + internal string ConfigFile { get { return configfile; } set { configfile = value; } } + internal DataLocationList Resources { get { return resources; } } + internal bool StrictPatches { get { return strictpatches; } set { strictpatches = value; } } + internal List ScriptFiles { get { return scriptfiles; } set { scriptfiles = value; } } + internal string PreviousName { get { return previousname; } set { previousname = value; } } + internal string CurrentName + { + get { return currentname; } + + set + { + // Change the name, but keep previous name + if (currentname != value) + { + if (previousname == "") previousname = currentname; + currentname = value; + } + } + } + + public string LevelName { get { return currentname; } } + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + internal MapOptions() + { + // Initialize + this.previousname = ""; + this.currentname = ""; + this.configfile = ""; + this.strictpatches = false; + this.resources = new DataLocationList(); + this.mapconfig = new Configuration(true); + this.scriptfiles = new List(); + } + + // Constructor to load from Doom Builder Map Settings Configuration + internal MapOptions(Configuration cfg, string mapname) + { + IDictionary mapinfo, resinfo; + DataLocation res; + + // Initialize + this.previousname = ""; + this.currentname = mapname; + this.strictpatches = General.Int2Bool(cfg.ReadSetting("strictpatches", 0)); + this.configfile = cfg.ReadSetting("gameconfig", ""); + this.resources = new DataLocationList(); + this.mapconfig = new Configuration(true); + this.scriptfiles = new List(); + + // Read map configuration + this.mapconfig.Root = cfg.ReadSetting("maps." + mapname, new Hashtable()); + + // Resources + IDictionary reslist = this.mapconfig.ReadSetting("resources", new Hashtable()); + foreach (DictionaryEntry mp in reslist) + { + // Item is a structure? + if (mp.Value is IDictionary) + { + // Create resource + resinfo = (IDictionary)mp.Value; + res = new DataLocation(); + + // Copy information from Configuration to ResourceLocation + if (resinfo.Contains("type") && (resinfo["type"] is int)) res.type = (int)resinfo["type"]; + if (resinfo.Contains("location") && (resinfo["location"] is string)) res.location = (string)resinfo["location"]; + if (resinfo.Contains("textures") && (resinfo["textures"] is bool)) res.option1 = (bool)resinfo["textures"]; + if (resinfo.Contains("flats") && (resinfo["flats"] is bool)) res.option2 = (bool)resinfo["flats"]; + + // Add resource + AddResource(res); + } + } + + // Scripts + IDictionary scplist = this.mapconfig.ReadSetting("scripts", new Hashtable()); + foreach (DictionaryEntry mp in scplist) + scriptfiles.Add(mp.Value.ToString()); + } + + ~MapOptions() + { + // Clean up + this.resources = null; + this.scriptfiles = null; + } + + #endregion + + #region ================== Methods + + // This makes the path prefix for the given assembly + private string GetPluginPathPrefix(Assembly asm) + { + Plugin p = General.Plugins.FindPluginByAssembly(asm); + return "plugins." + p.Name.ToLowerInvariant() + "."; + } + + // ReadPluginSetting + public string ReadPluginSetting(string setting, string defaultsetting) { return mapconfig.ReadSetting(GetPluginPathPrefix(Assembly.GetCallingAssembly()) + setting, defaultsetting); } + public int ReadPluginSetting(string setting, int defaultsetting) { return mapconfig.ReadSetting(GetPluginPathPrefix(Assembly.GetCallingAssembly()) + setting, defaultsetting); } + public float ReadPluginSetting(string setting, float defaultsetting) { return mapconfig.ReadSetting(GetPluginPathPrefix(Assembly.GetCallingAssembly()) + setting, defaultsetting); } + public short ReadPluginSetting(string setting, short defaultsetting) { return mapconfig.ReadSetting(GetPluginPathPrefix(Assembly.GetCallingAssembly()) + setting, defaultsetting); } + public long ReadPluginSetting(string setting, long defaultsetting) { return mapconfig.ReadSetting(GetPluginPathPrefix(Assembly.GetCallingAssembly()) + setting, defaultsetting); } + public bool ReadPluginSetting(string setting, bool defaultsetting) { return mapconfig.ReadSetting(GetPluginPathPrefix(Assembly.GetCallingAssembly()) + setting, defaultsetting); } + public byte ReadPluginSetting(string setting, byte defaultsetting) { return mapconfig.ReadSetting(GetPluginPathPrefix(Assembly.GetCallingAssembly()) + setting, defaultsetting); } + public IDictionary ReadPluginSetting(string setting, IDictionary defaultsetting) { return mapconfig.ReadSetting(GetPluginPathPrefix(Assembly.GetCallingAssembly()) + setting, defaultsetting); } + + // ReadPluginSetting with specific plugin + public string ReadPluginSetting(string pluginname, string setting, string defaultsetting) { return mapconfig.ReadSetting(pluginname.ToLowerInvariant() + "." + setting, defaultsetting); } + public int ReadPluginSetting(string pluginname, string setting, int defaultsetting) { return mapconfig.ReadSetting(pluginname.ToLowerInvariant() + "." + setting, defaultsetting); } + public float ReadPluginSetting(string pluginname, string setting, float defaultsetting) { return mapconfig.ReadSetting(pluginname.ToLowerInvariant() + "." + setting, defaultsetting); } + public short ReadPluginSetting(string pluginname, string setting, short defaultsetting) { return mapconfig.ReadSetting(pluginname.ToLowerInvariant() + "." + setting, defaultsetting); } + public long ReadPluginSetting(string pluginname, string setting, long defaultsetting) { return mapconfig.ReadSetting(pluginname.ToLowerInvariant() + "." + setting, defaultsetting); } + public bool ReadPluginSetting(string pluginname, string setting, bool defaultsetting) { return mapconfig.ReadSetting(pluginname.ToLowerInvariant() + "." + setting, defaultsetting); } + public byte ReadPluginSetting(string pluginname, string setting, byte defaultsetting) { return mapconfig.ReadSetting(pluginname.ToLowerInvariant() + "." + setting, defaultsetting); } + public IDictionary ReadPluginSetting(string pluginname, string setting, IDictionary defaultsetting) { return mapconfig.ReadSetting(pluginname.ToLowerInvariant() + "." + setting, defaultsetting); } + + // WritePluginSetting + public bool WritePluginSetting(string setting, object settingvalue) { return mapconfig.WriteSetting(GetPluginPathPrefix(Assembly.GetCallingAssembly()) + setting, settingvalue); } + + // DeletePluginSetting + public bool DeletePluginSetting(string setting) { return mapconfig.DeleteSetting(GetPluginPathPrefix(Assembly.GetCallingAssembly()) + setting); } + + // This stores the map options in a configuration + internal void WriteConfiguration(string settingsfile) + { + Configuration wadcfg; + + // Write resources to config + resources.WriteToConfig(mapconfig, "resources"); + + // Write grid settings + General.Map.Grid.WriteToConfig(mapconfig, "grid"); + + // Write scripts to config + mapconfig.DeleteSetting("scripts"); + for (int i = 0; i < scriptfiles.Count; i++) + mapconfig.WriteSetting("scripts." + "file" + i.ToString(CultureInfo.InvariantCulture), scriptfiles[i]); + + // Load the file or make a new file + if (File.Exists(settingsfile)) + wadcfg = new Configuration(settingsfile, true); + else + wadcfg = new Configuration(true); + + // Write configuration type information + wadcfg.WriteSetting("type", "Doom Builder Map Settings Configuration"); + wadcfg.WriteSetting("gameconfig", configfile); + wadcfg.WriteSetting("strictpatches", General.Bool2Int(strictpatches)); + + // Update the settings file with this map configuration + wadcfg.WriteSetting("maps." + currentname, mapconfig.Root); + + // Save file + wadcfg.SaveConfiguration(settingsfile); + } + + // This adds a resource location and returns the index where the item was added + internal int AddResource(DataLocation res) + { + // Get a fully qualified path + res.location = Path.GetFullPath(res.location); + + // Go for all items in the list + for (int i = 0; i < resources.Count; i++) + { + // Check if location is already added + if (Path.GetFullPath(resources[i].location) == res.location) + { + // Update the item in the list + resources[i] = res; + return i; + } + } + + // Add to list + resources.Add(res); + return resources.Count - 1; + } + + // This clears all reasource + internal void ClearResources() + { + // Clear list + resources.Clear(); + } + + // This removes a resource by index + internal void RemoveResource(int index) + { + // Remove the item + resources.RemoveAt(index); + } + + // This copies resources from a list + internal void CopyResources(DataLocationList fromlist) + { + // Clear this list + resources.Clear(); + resources.AddRange(fromlist); + } + + // This loads the grid settings + internal void ApplyGridSettings() + { + General.Map.Grid.ReadFromConfig(mapconfig, "grid"); + } + + // This displays the current map name + public override string ToString() + { + return currentname; + } + + // This returns the UDMF field type + internal int GetUniversalFieldType(string elementname, string fieldname, int defaulttype) + { + int type; + + // Check if the field type is set in the game configuration + type = General.Map.Config.ReadSetting("universalfields." + elementname + "." + fieldname + ".type", -1); + if (type == -1) + { + // Read from map configuration + type = mapconfig.ReadSetting("fieldtypes." + elementname + "." + fieldname, defaulttype); + } + + return type; + } + + // This stores the UDMF field type + internal void SetUniversalFieldType(string elementname, string fieldname, int type) + { + // Check if the type of this field is not set in the game configuration + if (General.Map.Config.ReadSetting("universalfields." + elementname + "." + fieldname + ".type", -1) == -1) + { + // Write type to map configuration + mapconfig.WriteSetting("fieldtypes." + elementname + "." + fieldname, type); + } + } + + // This removes all UDMF field types + internal void ForgetUniversalFieldTypes() + { + mapconfig.DeleteSetting("fieldtypes"); + } + + #endregion + } +} diff --git a/Source/Core/Map/MapSet.cs b/Source/Core/Map/MapSet.cs new file mode 100644 index 0000000..e2fcb77 --- /dev/null +++ b/Source/Core/Map/MapSet.cs @@ -0,0 +1,2936 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using CodeImp.DoomBuilder.Geometry; +using CodeImp.DoomBuilder.Windows; +using SlimDX.Direct3D9; +using CodeImp.DoomBuilder.Rendering; +using SlimDX; +using System.Drawing; +using CodeImp.DoomBuilder.Editing; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Types; +using System.IO; +using CodeImp.DoomBuilder.Config; +using CodeImp.DoomBuilder.VisualModes; + +#endregion + +namespace CodeImp.DoomBuilder.Map +{ + /// + /// Manages all geometry structures and things in a map. Also provides + /// methods to works with selections and marking elements for any purpose. + /// Note that most methods are of O(n) complexity. + /// + public sealed class MapSet + { + #region ================== Constants + + /// Stiching distance. This is only to get around inaccuracies. Basically, + /// geometry only stitches when exactly on top of each other. + public const float STITCH_DISTANCE = 0.001f; + + // Virtual sector identification + // This contains a character that is invalid in the UDMF standard, but valid + // in our parser, so that it can only be used by Doom Builder and will never + // conflict with any other valid UDMF field. + internal const string VIRTUAL_SECTOR_FIELD = "!virtual_sector"; + + // Handler for tag fields + public delegate void TagHandler(MapElement element, bool actionargument, UniversalType type, ref int value, T obj); + + #endregion + + #region ================== Variables + + // Sector indexing + private List indexholes; + private int lastsectorindex; + + // Sidedef indexing for (de)serialization + private Sidedef[] sidedefindices; + + // Map info + //private MapInfo mapinfo; // villsa + + // Map structures + private Vertex[] vertices; + private Linedef[] linedefs; + private Sidedef[] sidedefs; + private Sector[] sectors; + private Thing[] things; + private Macro[] macros; // villsa + private int numvertices; + private int numlinedefs; + private int numsidedefs; + private int numsectors; + private int numthings; + private int nummacros; // villsa + + // Behavior + private int freezearrays; + private bool autoremove; + + // Selected elements + private LinkedList sel_vertices; + private LinkedList sel_linedefs; + private LinkedList sel_sectors; + private LinkedList sel_things; + private SelectionType sel_type; + + // Statics + private static long emptylongname; + private static UniValue virtualsectorvalue; + + // Disposing + private bool isdisposed = false; + + #endregion + + #region ================== Properties + + //public MapInfo MapInfo { get { return mapinfo; } } // villsa + + /// Returns the number of selected sectors. + public int SelectedSectorsCount { get { return sel_sectors.Count; } } + + /// Returns the number of selected linedefs. + public int SelectedLinedefsCount { get { return sel_linedefs.Count; } } + + /// Returns the number of selected vertices. + public int SelectedVerticessCount { get { return sel_vertices.Count; } } + + /// Returns the number of selected things. + public int SelectedThingsCount { get { return sel_things.Count; } } + + /// Returns a reference to the list of all vertices. + public ICollection Vertices { get { if (freezearrays == 0) return vertices; else return new MapElementCollection(ref vertices, numvertices); } } + + /// Returns a reference to the list of all linedefs. + public ICollection Linedefs { get { if (freezearrays == 0) return linedefs; else return new MapElementCollection(ref linedefs, numlinedefs); } } + + /// Returns a reference to the list of all sidedefs. + public ICollection Sidedefs { get { if (freezearrays == 0) return sidedefs; else return new MapElementCollection(ref sidedefs, numsidedefs); } } + + /// Returns a reference to the list of all sectors. + public ICollection Sectors { get { if (freezearrays == 0) return sectors; else return new MapElementCollection(ref sectors, numsectors); } } + + /// Returns a reference to the list of all things. + public ICollection Things { get { if (freezearrays == 0) return things; else return new MapElementCollection(ref things, numthings); } } + + /// Indicates if the map is disposed. + public bool IsDisposed { get { return isdisposed; } } + + /// Returns a reference to the list of selected vertices. + internal LinkedList SelectedVertices { get { return sel_vertices; } } + + /// Returns a reference to the list of selected linedefs. + internal LinkedList SelectedLinedefs { get { return sel_linedefs; } } + + /// Returns a reference to the list of selected sectors. + internal LinkedList SelectedSectors { get { return sel_sectors; } } + + /// Returns a reference to the list of selected things. + internal LinkedList SelectedThings { get { return sel_things; } } + + internal Macro[] Macros { get { return macros; } } // villsa + + /// Returns the current type of selection. + public SelectionType SelectionType { get { return sel_type; } set { sel_type = value; } } + + /// Long name to indicate "no texture". This is the long name for a single dash. + public static long EmptyLongName { get { return emptylongname; } } + + /// Returns the name of the custom field that marks virtual sectors in pasted geometry. + public static string VirtualSectorField { get { return VIRTUAL_SECTOR_FIELD; } } + + /// Returns the value of the custom field that marks virtual sectors in pasted geometry. + public static UniValue VirtualSectorValue { get { return virtualsectorvalue; } } + + internal Sidedef[] SidedefIndices { get { return sidedefindices; } } + + internal bool AutoRemove { get { return autoremove; } set { autoremove = value; } } + + public int NumMacros { get { return nummacros; } set { nummacros = value; } } // villsa + + #endregion + + #region ================== Constructor / Disposer + + // Constructor for new empty map + internal MapSet() + { + // Initialize + //mapinfo = new MapInfo(); // villsa + vertices = new Vertex[0]; + linedefs = new Linedef[0]; + sidedefs = new Sidedef[0]; + sectors = new Sector[0]; + things = new Thing[0]; + macros = new Macro[256]; + sel_vertices = new LinkedList(); + sel_linedefs = new LinkedList(); + sel_sectors = new LinkedList(); + sel_things = new LinkedList(); + indexholes = new List(); + lastsectorindex = 0; + autoremove = true; + + // We have no destructor + GC.SuppressFinalize(this); + } + + // Constructor for map to deserialize + internal MapSet(MemoryStream stream) + { + // Initialize + //mapinfo = new MapInfo(); // villsa + vertices = new Vertex[0]; + linedefs = new Linedef[0]; + sidedefs = new Sidedef[0]; + sectors = new Sector[0]; + things = new Thing[0]; + macros = new Macro[256]; + sel_vertices = new LinkedList(); + sel_linedefs = new LinkedList(); + sel_sectors = new LinkedList(); + sel_things = new LinkedList(); + indexholes = new List(); + lastsectorindex = 0; + autoremove = true; + + // Deserialize + Deserialize(stream); + + // We have no destructor + GC.SuppressFinalize(this); + } + + // Disposer + internal void Dispose() + { + // Not already disposed? + if (!isdisposed) + { + // Already set isdisposed so that changes can be prohibited + isdisposed = true; + BeginAddRemove(); + + // Dispose all things + while ((things.Length > 0) && (things[0] != null)) + things[0].Dispose(); + + // Dispose all sectors + while ((sectors.Length > 0) && (sectors[0] != null)) + sectors[0].Dispose(); + + // Dispose all sidedefs + while ((sidedefs.Length > 0) && (sidedefs[0] != null)) + sidedefs[0].Dispose(); + + // Dispose all linedefs + while ((linedefs.Length > 0) && (linedefs[0] != null)) + linedefs[0].Dispose(); + + // Dispose all vertices + while ((vertices.Length > 0) && (vertices[0] != null)) + vertices[0].Dispose(); + + // Clean up + //mapinfo = null; // villsa + vertices = null; + linedefs = null; + sidedefs = null; + sectors = null; + things = null; + macros = null; + sel_vertices = null; + sel_linedefs = null; + sel_sectors = null; + sel_things = null; + indexholes = null; + + // Done + isdisposed = true; + } + } + + // Static initializer + internal static void Initialize() + { + emptylongname = Lump.MakeLongName("-"); + virtualsectorvalue = new UniValue((int)UniversalType.Integer, (int)0); + } + + #endregion + + #region ================== Management + + // This begins large add/remove operations + public void BeginAddRemove() + { + freezearrays++; + } + + // This allocates the arrays to a minimum size so that + // a lot of items can be created faster. This function will never + // allocate less than the current number of items. + public void SetCapacity(int nvertices, int nlinedefs, int nsidedefs, int nsectors, int nthings) + { + if (freezearrays == 0) + throw new Exception("You must call BeginAddRemove before setting the reserved capacity."); + + if (numvertices < nvertices) + Array.Resize(ref vertices, nvertices); + + if (numlinedefs < nlinedefs) + Array.Resize(ref linedefs, nlinedefs); + + if (numsidedefs < nsidedefs) + Array.Resize(ref sidedefs, nsidedefs); + + if (numsectors < nsectors) + Array.Resize(ref sectors, nsectors); + + if (numthings < nthings) + Array.Resize(ref things, nthings); + } + + // This ends add/remove operations and crops the arrays + public void EndAddRemove() + { + if (freezearrays > 0) + freezearrays--; + + if (freezearrays == 0) + { + if (numvertices < vertices.Length) + Array.Resize(ref vertices, numvertices); + + if (numlinedefs < linedefs.Length) + Array.Resize(ref linedefs, numlinedefs); + + if (numsidedefs < sidedefs.Length) + Array.Resize(ref sidedefs, numsidedefs); + + if (numsectors < sectors.Length) + Array.Resize(ref sectors, numsectors); + + if (numthings < things.Length) + Array.Resize(ref things, numthings); + } + } + + /// + /// This makes a deep copy and returns the new MapSet. + /// + public MapSet Clone() + { + Linedef nl; + Sidedef nd; + + // Create the map set + MapSet newset = new MapSet(); + newset.BeginAddRemove(); + newset.SetCapacity(numvertices, numlinedefs, numsidedefs, numsectors, numthings); + + // Go for all vertices + foreach (Vertex v in vertices) + { + // Make new vertex + v.Clone = newset.CreateVertex(v.Position); + v.CopyPropertiesTo(v.Clone); + } + + // Go for all sectors + foreach (Sector s in sectors) + { + // Make new sector + s.Clone = newset.CreateSector(); + s.CopyPropertiesTo(s.Clone); + } + + // Go for all linedefs + foreach (Linedef l in linedefs) + { + // Make new linedef + nl = newset.CreateLinedef(l.Start.Clone, l.End.Clone); + l.CopyPropertiesTo(nl); + + // Linedef has a front side? + if (l.Front != null) + { + // Make new sidedef + nd = newset.CreateSidedef(nl, true, l.Front.Sector.Clone); + l.Front.CopyPropertiesTo(nd); + } + + // Linedef has a back side? + if (l.Back != null) + { + // Make new sidedef + nd = newset.CreateSidedef(nl, false, l.Back.Sector.Clone); + l.Back.CopyPropertiesTo(nd); + } + } + + // Go for all things + foreach (Thing t in things) + { + // Make new thing + Thing nt = newset.CreateThing(); + t.CopyPropertiesTo(nt); + } + + // Remove clone references + foreach (Vertex v in vertices) v.Clone = null; + foreach (Sector s in sectors) s.Clone = null; + + // Return the new set + newset.EndAddRemove(); + return newset; + } + + // This makes a deep copy of the marked geometry and binds missing sectors to a virtual sector + internal MapSet CloneMarked() + { + Sector virtualsector = null; + + // Create the map set + MapSet newset = new MapSet(); + newset.BeginAddRemove(); + + // Get marked geometry + ICollection mvertices = GetMarkedVertices(true); + ICollection mlinedefs = GetMarkedLinedefs(true); + ICollection msectors = GetMarkedSectors(true); + ICollection mthings = GetMarkedThings(true); + newset.SetCapacity(mvertices.Count, mlinedefs.Count, numsidedefs, msectors.Count, mthings.Count); + + // Go for all vertices + foreach (Vertex v in mvertices) + { + // Make new vertex + v.Clone = newset.CreateVertex(v.Position); + v.CopyPropertiesTo(v.Clone); + } + + // Go for all sectors + foreach (Sector s in msectors) + { + // Make new sector + s.Clone = newset.CreateSector(); + s.CopyPropertiesTo(s.Clone); + } + + // Go for all linedefs + foreach (Linedef l in mlinedefs) + { + // Make new linedef + Linedef nl = newset.CreateLinedef(l.Start.Clone, l.End.Clone); + l.CopyPropertiesTo(nl); + + // Linedef has a front side? + if (l.Front != null) + { + Sidedef nd; + + // Sector on front side marked? + if (l.Front.Sector.Marked) + { + // Make new sidedef + nd = newset.CreateSidedef(nl, true, l.Front.Sector.Clone); + } + else + { + // Make virtual sector if needed + if (virtualsector == null) + { + virtualsector = newset.CreateSector(); + l.Front.Sector.CopyPropertiesTo(virtualsector); + virtualsector.Fields.BeforeFieldsChange(); + virtualsector.Fields[VIRTUAL_SECTOR_FIELD] = new UniValue(virtualsectorvalue); + } + + // Make new sidedef that links to the virtual sector + nd = newset.CreateSidedef(nl, true, virtualsector); + } + + l.Front.CopyPropertiesTo(nd); + } + + // Linedef has a back side? + if (l.Back != null) + { + Sidedef nd; + + // Sector on front side marked? + if (l.Back.Sector.Marked) + { + // Make new sidedef + nd = newset.CreateSidedef(nl, false, l.Back.Sector.Clone); + } + else + { + // Make virtual sector if needed + if (virtualsector == null) + { + virtualsector = newset.CreateSector(); + l.Back.Sector.CopyPropertiesTo(virtualsector); + virtualsector.Fields.BeforeFieldsChange(); + virtualsector.Fields[VIRTUAL_SECTOR_FIELD] = new UniValue(virtualsectorvalue); + } + + // Make new sidedef that links to the virtual sector + nd = newset.CreateSidedef(nl, false, virtualsector); + } + + l.Back.CopyPropertiesTo(nd); + } + } + + // Go for all things + foreach (Thing t in mthings) + { + // Make new thing + Thing nt = newset.CreateThing(); + t.CopyPropertiesTo(nt); + } + + // Remove clone references + foreach (Vertex v in vertices) v.Clone = null; + foreach (Sector s in sectors) s.Clone = null; + + // Return the new set + newset.EndAddRemove(); + return newset; + } + + /// This creates a new vertex and returns it. + public Vertex CreateVertex(Vector2D pos) + { + if (numvertices == General.Map.FormatInterface.MaxVertices) + { + General.Interface.DisplayStatus(StatusType.Warning, "Failed to complete operation: maximum number of vertices reached."); + return null; + } + + // Make the vertex + Vertex v = new Vertex(this, numvertices, pos); + AddItem(v, ref vertices, numvertices, ref numvertices); + return v; + } + + /// This creates a new vertex and returns it. + public Vertex CreateVertex(int index, Vector2D pos) + { + if (numvertices == General.Map.FormatInterface.MaxVertices) + { + General.Interface.DisplayStatus(StatusType.Warning, "Failed to complete operation: maximum number of vertices reached."); + return null; + } + + // Make the vertex + Vertex v = new Vertex(this, index, pos); + AddItem(v, ref vertices, index, ref numvertices); + return v; + } + + /// This creates a new linedef and returns it. + public Linedef CreateLinedef(Vertex start, Vertex end) + { + if (numlinedefs == General.Map.FormatInterface.MaxLinedefs) + { + General.Interface.DisplayStatus(StatusType.Warning, "Failed to complete operation: maximum number of linedefs reached."); + return null; + } + + // Make the linedef + Linedef l = new Linedef(this, numlinedefs, start, end); + AddItem(l, ref linedefs, numlinedefs, ref numlinedefs); + return l; + } + + /// This creates a new linedef and returns it. + public Linedef CreateLinedef(int index, Vertex start, Vertex end) + { + if (numlinedefs == General.Map.FormatInterface.MaxLinedefs) + { + General.Interface.DisplayStatus(StatusType.Warning, "Failed to complete operation: maximum number of linedefs reached."); + return null; + } + + // Make the linedef + Linedef l = new Linedef(this, index, start, end); + AddItem(l, ref linedefs, index, ref numlinedefs); + return l; + } + + /// This creates a new sidedef and returns it. + public Sidedef CreateSidedef(Linedef l, bool front, Sector s) + { + if (numsidedefs == int.MaxValue) + { + General.Interface.DisplayStatus(StatusType.Warning, "Failed to complete operation: maximum number of sidedefs reached."); + return null; + } + + // Make the sidedef + Sidedef sd = new Sidedef(this, numsidedefs, l, front, s); + AddItem(sd, ref sidedefs, numsidedefs, ref numsidedefs); + return sd; + } + + /// This creates a new sidedef and returns it. + public Sidedef CreateSidedef(int index, Linedef l, bool front, Sector s) + { + if (numsidedefs == int.MaxValue) + { + General.Interface.DisplayStatus(StatusType.Warning, "Failed to complete operation: maximum number of sidedefs reached."); + return null; + } + + // Make the sidedef + Sidedef sd = new Sidedef(this, index, l, front, s); + AddItem(sd, ref sidedefs, index, ref numsidedefs); + return sd; + } + + /// This creates a new sector and returns it. + public Sector CreateSector() + { + // Make the sector + return CreateSector(numsectors); + } + + /// This creates a new sector and returns it. + public Sector CreateSector(int index) + { + int fixedindex; + + if (numsectors == General.Map.FormatInterface.MaxSectors) + { + General.Interface.DisplayStatus(StatusType.Warning, "Failed to complete operation: maximum number of sectors reached."); + return null; + } + + // Do we have any index holes we can use? + if (indexholes.Count > 0) + { + // Take one of the index holes + fixedindex = indexholes[indexholes.Count - 1]; + indexholes.RemoveAt(indexholes.Count - 1); + } + else + { + // Make a new index + fixedindex = lastsectorindex++; + } + + // Make the sector + return CreateSectorEx(fixedindex, index); + } + + // This creates a new sector with a specific fixed index + private Sector CreateSectorEx(int fixedindex, int index) + { + if (numsectors == General.Map.FormatInterface.MaxSectors) + { + General.Interface.DisplayStatus(StatusType.Warning, "Failed to complete operation: maximum number of sectors reached."); + return null; + } + + // Make the sector + Sector s = new Sector(this, index, fixedindex); + AddItem(s, ref sectors, index, ref numsectors); + return s; + } + + /// This creates a new thing and returns it. + public Thing CreateThing() + { + if (numthings == General.Map.FormatInterface.MaxThings) + { + General.Interface.DisplayStatus(StatusType.Warning, "Failed to complete operation: maximum number of things reached."); + return null; + } + + // Make the thing + Thing t = new Thing(this, numthings); + AddItem(t, ref things, numthings, ref numthings); + return t; + } + + /// This creates a new thing and returns it. + public Thing CreateThing(int index) + { + if (numthings == General.Map.FormatInterface.MaxThings) + { + General.Interface.DisplayStatus(StatusType.Warning, "Failed to complete operation: maximum number of things reached."); + return null; + } + + // Make the thing + Thing t = new Thing(this, index); + AddItem(t, ref things, index, ref numthings); + return t; + } + + // This increases the size of the array to add an item + private void AddItem(T item, ref T[] array, int index, ref int counter) where T : MapElement + { + // Only resize when there are no more free entries + if (counter == array.Length) + { + if (freezearrays == 0) + Array.Resize(ref array, counter + 1); + else + Array.Resize(ref array, counter + 10); + } + + // Move item at the given index if the new item is not added at the end + if (index != counter) + { + array[counter] = array[index]; + array[counter].Index = counter; + } + + // Add item + array[index] = item; + counter++; + } + + // This adds a sector index hole + internal void AddSectorIndexHole(int index) + { + indexholes.Add(index); + } + + private void RemoveItem(ref T[] array, int index, ref int counter) where T : MapElement + { + if (index == (counter - 1)) + { + array[index] = null; + } + else + { + array[index] = array[counter - 1]; + array[index].Index = index; + array[counter - 1] = null; + } + + counter--; + + if (freezearrays == 0) + Array.Resize(ref array, counter); + } + + internal void RemoveVertex(int index) + { + RemoveItem(ref vertices, index, ref numvertices); + } + + internal void RemoveLinedef(int index) + { + RemoveItem(ref linedefs, index, ref numlinedefs); + } + + internal void RemoveSidedef(int index) + { + RemoveItem(ref sidedefs, index, ref numsidedefs); + } + + internal void RemoveSector(int index) + { + RemoveItem(ref sectors, index, ref numsectors); + } + + internal void RemoveThing(int index) + { + RemoveItem(ref things, index, ref numthings); + } + + #endregion + + #region ================== Serialization + + // This serializes the MapSet + internal MemoryStream Serialize() + { + MemoryStream stream = new MemoryStream(20000000); // Yes that is about 20 MB. + SerializerStream serializer = new SerializerStream(stream); + + // Index the sidedefs + int sidedefindex = 0; + foreach (Sidedef sd in sidedefs) + sd.SerializedIndex = sidedefindex++; + + serializer.Begin(); + + // Write private data + serializer.wInt(lastsectorindex); + serializer.wInt(indexholes.Count); + foreach (int i in indexholes) serializer.wInt(i); + + // Write map data + WriteVertices(serializer); + WriteSectors(serializer); + WriteLinedefs(serializer); + WriteSidedefs(serializer); + WriteThings(serializer); + + serializer.End(); + + // Reallocate to keep only the used memory + stream.Capacity = (int)stream.Length; + + return stream; + } + + // This serializes things + private void WriteThings(SerializerStream stream) + { + stream.wInt(numthings); + + // Go for all things + foreach (Thing t in things) + { + t.ReadWrite(stream); + } + } + + // This serializes vertices + private void WriteVertices(SerializerStream stream) + { + stream.wInt(numvertices); + + // Go for all vertices + int index = 0; + foreach (Vertex v in vertices) + { + v.SerializedIndex = index++; + + v.ReadWrite(stream); + } + } + + // This serializes linedefs + private void WriteLinedefs(SerializerStream stream) + { + stream.wInt(numlinedefs); + + // Go for all lines + int index = 0; + foreach (Linedef l in linedefs) + { + l.SerializedIndex = index++; + + stream.wInt(l.Start.SerializedIndex); + + stream.wInt(l.End.SerializedIndex); + + l.ReadWrite(stream); + } + } + + // This serializes sidedefs + private void WriteSidedefs(SerializerStream stream) + { + stream.wInt(numsidedefs); + + // Go for all sidedefs + foreach (Sidedef sd in sidedefs) + { + stream.wInt(sd.Line.SerializedIndex); + + stream.wInt(sd.Sector.SerializedIndex); + + stream.wBool(sd.IsFront); + + sd.ReadWrite(stream); + } + } + + // This serializes sectors + private void WriteSectors(SerializerStream stream) + { + stream.wInt(numsectors); + + // Go for all sectors + int index = 0; + foreach (Sector s in sectors) + { + s.SerializedIndex = index++; + + s.ReadWrite(stream); + } + } + + #endregion + + #region ================== Deserialization + + // This serializes the MapSet + private void Deserialize(MemoryStream stream) + { + stream.Seek(0, SeekOrigin.Begin); + DeserializerStream deserializer = new DeserializerStream(stream); + + deserializer.Begin(); + + // Read private data + int c; + deserializer.rInt(out lastsectorindex); + deserializer.rInt(out c); + indexholes = new List(c); + for (int i = 0; i < c; i++) + { + int index; deserializer.rInt(out index); + indexholes.Add(index); + } + + // Read map data + Vertex[] verticesarray = ReadVertices(deserializer); + Sector[] sectorsarray = ReadSectors(deserializer); + Linedef[] linedefsarray = ReadLinedefs(deserializer, verticesarray); + ReadSidedefs(deserializer, linedefsarray, sectorsarray); + ReadThings(deserializer); + + deserializer.End(); + + // Make table of sidedef indices + sidedefindices = new Sidedef[numsidedefs]; + foreach (Sidedef sd in sidedefs) + sidedefindices[sd.SerializedIndex] = sd; + + // Call PostDeserialize + foreach (Sector s in sectors) + s.PostDeserialize(this); + } + + // This deserializes things + private void ReadThings(DeserializerStream stream) + { + int c; stream.rInt(out c); + + // Go for all things + for (int i = 0; i < c; i++) + { + Thing t = CreateThing(); + t.ReadWrite(stream); + } + } + + // This deserializes vertices + private Vertex[] ReadVertices(DeserializerStream stream) + { + int c; stream.rInt(out c); + + Vertex[] array = new Vertex[c]; + + // Go for all vertices + for (int i = 0; i < c; i++) + { + array[i] = CreateVertex(new Vector2D()); + array[i].ReadWrite(stream); + } + + return array; + } + + // This deserializes linedefs + private Linedef[] ReadLinedefs(DeserializerStream stream, Vertex[] verticesarray) + { + int c; stream.rInt(out c); + + Linedef[] array = new Linedef[c]; + + // Go for all lines + for (int i = 0; i < c; i++) + { + int start, end; + + stream.rInt(out start); + + stream.rInt(out end); + + array[i] = CreateLinedef(verticesarray[start], verticesarray[end]); + array[i].ReadWrite(stream); + } + + return array; + } + + // This deserializes sidedefs + private void ReadSidedefs(DeserializerStream stream, Linedef[] linedefsarray, Sector[] sectorsarray) + { + int c; stream.rInt(out c); + + // Go for all sidedefs + for (int i = 0; i < c; i++) + { + int lineindex, sectorindex; + bool front; + + stream.rInt(out lineindex); + + stream.rInt(out sectorindex); + + stream.rBool(out front); + + Sidedef sd = CreateSidedef(linedefsarray[lineindex], front, sectorsarray[sectorindex]); + sd.ReadWrite(stream); + } + } + + // This deserializes sectors + private Sector[] ReadSectors(DeserializerStream stream) + { + int c; stream.rInt(out c); + + Sector[] array = new Sector[c]; + + // Go for all sectors + for (int i = 0; i < c; i++) + { + array[i] = CreateSector(); + array[i].ReadWrite(stream); + } + + return array; + } + + #endregion + + #region ================== Updating + + /// + /// This updates the cache of all elements where needed. You must call this after making changes to the map. + /// + public void Update() + { + // Update all! + Update(true, true); + } + + /// + /// This updates the cache of all elements where needed. It is not recommended to use this version, please use Update() instead. + /// + public void Update(bool dolines, bool dosectors) + { + // Update all linedefs + if (dolines) foreach (Linedef l in linedefs) l.UpdateCache(); + + // Update all sectors + if (dosectors) + { + foreach (Sector s in sectors) s.Triangulate(); + + General.Map.CRenderer2D.Surfaces.AllocateBuffers(); + + foreach (Sector s in sectors) s.CreateSurfaces(); + + General.Map.CRenderer2D.Surfaces.UnlockBuffers(); + } + } + + /// + /// This updates the cache of all elements that is required after a configuration or settings change. + /// + public void UpdateConfiguration() + { + // Update all things + foreach (Thing t in things) t.UpdateConfiguration(); + } + + #endregion + + #region ================== Selection + + // This checks a flag in a selection type + private bool InSelectionType(SelectionType value, SelectionType bits) + { + return (value & bits) == bits; + } + + /// This converts the current selection to a different type of selection as specified. + /// Note that this function uses the markings to convert the selection. + public void ConvertSelection(SelectionType target) + { + ConvertSelection(SelectionType.All, target); + } + + /// This converts the current selection to a different type of selection as specified. + /// Note that this function uses the markings to convert the selection. + public void ConvertSelection(SelectionType source, SelectionType target) + { + ICollection lines; + ICollection verts; + + ClearAllMarks(false); + + switch (target) + { + // Convert geometry selection to vertices only + case SelectionType.Vertices: + if (InSelectionType(source, SelectionType.Linedefs)) MarkSelectedLinedefs(true, true); + if (InSelectionType(source, SelectionType.Sectors)) General.Map.Map.MarkSelectedSectors(true, true); + verts = General.Map.Map.GetVerticesFromLinesMarks(true); + foreach (Vertex v in verts) v.Selected = true; + verts = General.Map.Map.GetVerticesFromSectorsMarks(true); + foreach (Vertex v in verts) v.Selected = true; + General.Map.Map.ClearSelectedSectors(); + General.Map.Map.ClearSelectedLinedefs(); + break; + + // Convert geometry selection to linedefs only + case SelectionType.Linedefs: + if (InSelectionType(source, SelectionType.Vertices)) MarkSelectedVertices(true, true); + if (!InSelectionType(source, SelectionType.Linedefs)) ClearSelectedLinedefs(); + lines = General.Map.Map.LinedefsFromMarkedVertices(false, true, false); + foreach (Linedef l in lines) l.Selected = true; + if (InSelectionType(source, SelectionType.Sectors)) + { + foreach (Sector s in General.Map.Map.Sectors) + { + if (s.Selected) + { + foreach (Sidedef sd in s.Sidedefs) + sd.Line.Selected = true; + } + } + } + General.Map.Map.ClearSelectedSectors(); + General.Map.Map.ClearSelectedVertices(); + break; + + // Convert geometry selection to sectors only + case SelectionType.Sectors: + if (InSelectionType(source, SelectionType.Vertices)) MarkSelectedVertices(true, true); + if (!InSelectionType(source, SelectionType.Linedefs)) ClearSelectedLinedefs(); + lines = LinedefsFromMarkedVertices(false, true, false); + foreach (Linedef l in lines) l.Selected = true; + ClearMarkedSectors(true); + foreach (Linedef l in linedefs) + { + if (!l.Selected) + { + if (l.Front != null) l.Front.Sector.Marked = false; + if (l.Back != null) l.Back.Sector.Marked = false; + } + } + ClearSelectedLinedefs(); + ClearSelectedVertices(); + if (InSelectionType(source, SelectionType.Sectors)) + { + foreach (Sector s in General.Map.Map.Sectors) + { + if (s.Marked || s.Selected) + { + s.Selected = true; + foreach (Sidedef sd in s.Sidedefs) + sd.Line.Selected = true; + } + } + } + else + { + foreach (Sector s in General.Map.Map.Sectors) + { + if (s.Marked) + { + s.Selected = true; + foreach (Sidedef sd in s.Sidedefs) + sd.Line.Selected = true; + } + else + { + s.Selected = false; + } + } + } + break; + + default: + throw new ArgumentException("Unsupported selection target conversion"); + break; + } + + // New selection type + sel_type = target; + } + + /// This clears all selected items + public void ClearAllSelected() + { + ClearSelectedVertices(); + ClearSelectedThings(); + ClearSelectedLinedefs(); + ClearSelectedSectors(); + } + + /// This clears selected vertices. + public void ClearSelectedVertices() + { + sel_vertices.Clear(); + foreach (Vertex v in vertices) v.Selected = false; + } + + /// This clears selected things. + public void ClearSelectedThings() + { + sel_things.Clear(); + foreach (Thing t in things) t.Selected = false; + } + + /// This clears selected linedefs. + public void ClearSelectedLinedefs() + { + sel_linedefs.Clear(); + foreach (Linedef l in linedefs) l.Selected = false; + } + + /// This clears selected sectors. + public void ClearSelectedSectors() + { + sel_sectors.Clear(); + foreach (Sector s in sectors) s.Selected = false; + } + + /// Returns a collection of vertices that match a selected state. + public ICollection GetSelectedVertices(bool selected) + { + if (selected) + { + return new List(sel_vertices); + } + else + { + List list = new List(numvertices - sel_vertices.Count); + foreach (Vertex v in vertices) if (!v.Selected) list.Add(v); + return list; + } + } + + /// Returns a collection of things that match a selected state. + public ICollection GetSelectedThings(bool selected) + { + if (selected) + { + return new List(sel_things); + } + else + { + List list = new List(numthings - sel_things.Count); + foreach (Thing t in things) if (!t.Selected) list.Add(t); + return list; + } + } + + /// Returns a collection of linedefs that match a selected state. + public ICollection GetSelectedLinedefs(bool selected) + { + if (selected) + { + return new List(sel_linedefs); + } + else + { + List list = new List(numlinedefs - sel_linedefs.Count); + foreach (Linedef l in linedefs) if (!l.Selected) list.Add(l); + return list; + } + } + + /// Returns a collection of sidedefs that match a selected linedefs state. + public ICollection GetSidedefsFromSelectedLinedefs(bool selected) + { + if (selected) + { + List list = new List(sel_linedefs.Count); + foreach (Linedef ld in sel_linedefs) + { + if (ld.Front != null) list.Add(ld.Front); + if (ld.Back != null) list.Add(ld.Back); + } + return list; + } + else + { + List list = new List(numlinedefs - sel_linedefs.Count); + foreach (Linedef ld in linedefs) + { + if (!ld.Selected && (ld.Front != null)) list.Add(ld.Front); + if (!ld.Selected && (ld.Back != null)) list.Add(ld.Back); + } + return list; + } + } + + /// Returns a collection of sectors that match a selected state. + public ICollection GetSelectedSectors(bool selected) + { + if (selected) + { + return new List(sel_sectors); + } + else + { + List list = new List(numsectors - sel_sectors.Count); + foreach (Sector s in sectors) if (!s.Selected) list.Add(s); + return list; + } + } + + /// This selects or deselectes geometry based on marked elements. + public void SelectMarkedGeometry(bool mark, bool select) + { + SelectMarkedVertices(mark, select); + SelectMarkedLinedefs(mark, select); + SelectMarkedSectors(mark, select); + SelectMarkedThings(mark, select); + } + + /// This selects or deselectes geometry based on marked elements. + public void SelectMarkedVertices(bool mark, bool select) + { + foreach (Vertex v in vertices) if (v.Marked == mark) v.Selected = select; + } + + /// This selects or deselectes geometry based on marked elements. + public void SelectMarkedLinedefs(bool mark, bool select) + { + foreach (Linedef l in linedefs) if (l.Marked == mark) l.Selected = select; + } + + /// This selects or deselectes geometry based on marked elements. + public void SelectMarkedSectors(bool mark, bool select) + { + foreach (Sector s in sectors) if (s.Marked == mark) s.Selected = select; + } + + /// This selects or deselectes geometry based on marked elements. + public void SelectMarkedThings(bool mark, bool select) + { + foreach (Thing t in things) if (t.Marked == mark) t.Selected = select; + } + + /// This selects geometry by selection group index. + public void SelectVerticesByGroup(int groupmask) + { + foreach (SelectableElement e in vertices) e.SelectByGroup(groupmask); + } + + /// This selects geometry by selection group index. + public void SelectLinedefsByGroup(int groupmask) + { + foreach (SelectableElement e in linedefs) e.SelectByGroup(groupmask); + } + + /// This selects geometry by selection group index. + public void SelectSectorsByGroup(int groupmask) + { + foreach (SelectableElement e in sectors) e.SelectByGroup(groupmask); + } + + /// This selects geometry by selection group index. + public void SelectThingsByGroup(int groupmask) + { + foreach (SelectableElement e in things) e.SelectByGroup(groupmask); + } + + /// This adds the current selection to the specified selection group. + public void AddSelectionToGroup(int groupmask) + { + foreach (SelectableElement e in vertices) + if (e.Selected) e.AddToGroup(groupmask); + + foreach (SelectableElement e in linedefs) + if (e.Selected) e.AddToGroup(groupmask); + + foreach (SelectableElement e in sectors) + if (e.Selected) e.AddToGroup(groupmask); + + foreach (SelectableElement e in things) + if (e.Selected) e.AddToGroup(groupmask); + } + + #endregion + + #region ================== Marking + + /// This clears all marks on all elements. + public void ClearAllMarks(bool mark) + { + ClearMarkedVertices(mark); + ClearMarkedThings(mark); + ClearMarkedLinedefs(mark); + ClearMarkedSectors(mark); + ClearMarkedSidedefs(mark); + } + + /// This clears all marks on all vertices. + public void ClearMarkedVertices(bool mark) + { + foreach (Vertex v in vertices) v.Marked = mark; + } + + /// This clears all marks on all things. + public void ClearMarkedThings(bool mark) + { + foreach (Thing t in things) t.Marked = mark; + } + + /// This clears all marks on all linedefs. + public void ClearMarkedLinedefs(bool mark) + { + foreach (Linedef l in linedefs) l.Marked = mark; + } + + /// This clears all marks on all sidedefs. + public void ClearMarkedSidedefs(bool mark) + { + foreach (Sidedef s in sidedefs) s.Marked = mark; + } + + /// This clears all marks on all sectors. + public void ClearMarkedSectors(bool mark) + { + foreach (Sector s in sectors) s.Marked = mark; + } + + /// This inverts all marks on all elements. + public void InvertAllMarks() + { + InvertMarkedVertices(); + InvertMarkedThings(); + InvertMarkedLinedefs(); + InvertMarkedSectors(); + InvertMarkedSidedefs(); + } + + /// This inverts all marks on all vertices. + public void InvertMarkedVertices() + { + foreach (Vertex v in vertices) v.Marked = !v.Marked; + } + + /// This inverts all marks on all things. + public void InvertMarkedThings() + { + foreach (Thing t in things) t.Marked = !t.Marked; + } + + /// This inverts all marks on all linedefs. + public void InvertMarkedLinedefs() + { + foreach (Linedef l in linedefs) l.Marked = !l.Marked; + } + + /// This inverts all marks on all sidedefs. + public void InvertMarkedSidedefs() + { + foreach (Sidedef s in sidedefs) s.Marked = !s.Marked; + } + + /// This inverts all marks on all sectors. + public void InvertMarkedSectors() + { + foreach (Sector s in sectors) s.Marked = !s.Marked; + } + + /// Returns a collection of vertices that match a marked state. + public List GetMarkedVertices(bool mark) + { + List list = new List(numvertices >> 1); + foreach (Vertex v in vertices) if (v.Marked == mark) list.Add(v); + return list; + } + + /// Returns a collection of things that match a marked state. + public List GetMarkedThings(bool mark) + { + List list = new List(numthings >> 1); + foreach (Thing t in things) if (t.Marked == mark) list.Add(t); + return list; + } + + /// Returns a collection of linedefs that match a marked state. + public List GetMarkedLinedefs(bool mark) + { + List list = new List(numlinedefs >> 1); + foreach (Linedef l in linedefs) if (l.Marked == mark) list.Add(l); + return list; + } + + /// Returns a collection of sidedefs that match a marked state. + public List GetMarkedSidedefs(bool mark) + { + List list = new List(numsidedefs >> 1); + foreach (Sidedef s in sidedefs) if (s.Marked == mark) list.Add(s); + return list; + } + + /// Returns a collection of sectors that match a marked state. + public List GetMarkedSectors(bool mark) + { + List list = new List(numsectors >> 1); + foreach (Sector s in sectors) if (s.Marked == mark) list.Add(s); + return list; + } + + /// This marks vertices based on selected vertices. + public void MarkSelectedVertices(bool selected, bool mark) + { + foreach (Vertex v in sel_vertices) v.Marked = mark; + } + + /// This marks linedefs based on selected linedefs. + public void MarkSelectedLinedefs(bool selected, bool mark) + { + foreach (Linedef l in sel_linedefs) l.Marked = mark; + } + + /// This marks sectors based on selected sectors. + public void MarkSelectedSectors(bool selected, bool mark) + { + foreach (Sector s in sel_sectors) s.Marked = mark; + } + + /// This marks things based on selected things. + public void MarkSelectedThings(bool selected, bool mark) + { + foreach (Thing t in sel_things) t.Marked = mark; + } + + /// + /// This marks the front and back sidedefs on linedefs with the matching mark. + /// + public void MarkSidedefsFromLinedefs(bool matchmark, bool setmark) + { + foreach (Linedef l in linedefs) + { + if (l.Marked == matchmark) + { + if (l.Front != null) l.Front.Marked = setmark; + if (l.Back != null) l.Back.Marked = setmark; + } + } + } + + /// + /// This marks the sidedefs that make up the sectors with the matching mark. + /// + public void MarkSidedefsFromSectors(bool matchmark, bool setmark) + { + foreach (Sidedef sd in sidedefs) + { + if (sd.Sector.Marked == matchmark) sd.Marked = setmark; + } + } + + /// + /// Returns a collection of vertices that match a marked state on the linedefs. + /// + public ICollection GetVerticesFromLinesMarks(bool mark) + { + List list = new List(numvertices >> 1); + foreach (Vertex v in vertices) + { + foreach (Linedef l in v.Linedefs) + { + if (l.Marked == mark) + { + list.Add(v); + break; + } + } + } + return list; + } + + /// + /// Returns a collection of vertices that match a marked state on the linedefs. + /// The difference with GetVerticesFromLinesMarks is that in this method + /// ALL linedefs of a vertex must match the specified marked state. + /// + public ICollection GetVerticesFromAllLinesMarks(bool mark) + { + List list = new List(numvertices >> 1); + foreach (Vertex v in vertices) + { + bool qualified = true; + foreach (Linedef l in v.Linedefs) + { + if (l.Marked != mark) + { + qualified = false; + break; + } + } + if (qualified) list.Add(v); + } + return list; + } + + /// + /// Returns a collection of vertices that match a marked state on the linedefs. + /// + public ICollection GetVerticesFromSectorsMarks(bool mark) + { + List list = new List(numvertices >> 1); + foreach (Vertex v in vertices) + { + foreach (Linedef l in v.Linedefs) + { + if (((l.Front != null) && (l.Front.Sector.Marked == mark)) || + ((l.Back != null) && (l.Back.Sector.Marked == mark))) + { + list.Add(v); + break; + } + } + } + return list; + } + + /// + /// This marks all selected geometry, including sidedefs from sectors. + /// When sidedefsfromsectors is true, then the sidedefs are marked according to the + /// marked sectors. Otherwise the sidedefs are marked according to the marked linedefs. + /// + public void MarkAllSelectedGeometry(bool mark, bool linedefsfromvertices, bool verticesfromlinedefs, bool sectorsfromlinedefs, bool sidedefsfromsectors) + { + General.Map.Map.ClearAllMarks(!mark); + + // Direct vertices + General.Map.Map.MarkSelectedVertices(true, mark); + + // Direct linedefs + General.Map.Map.MarkSelectedLinedefs(true, mark); + + // Linedefs from vertices + // We do this before "vertices from lines" because otherwise we get lines marked that we didn't select + if (linedefsfromvertices) + { + ICollection lines = General.Map.Map.LinedefsFromMarkedVertices(!mark, mark, !mark); + foreach (Linedef l in lines) l.Marked = mark; + } + + // Vertices from linedefs + if (verticesfromlinedefs) + { + ICollection verts = General.Map.Map.GetVerticesFromLinesMarks(mark); + foreach (Vertex v in verts) v.Marked = mark; + } + + // Mark sectors from linedefs (note: this must be the first to mark + // sectors, because this clears the sector marks!) + if (sectorsfromlinedefs) + { + General.Map.Map.ClearMarkedSectors(mark); + foreach (Linedef l in General.Map.Map.Linedefs) + { + if (!l.Selected) + { + if (l.Front != null) l.Front.Sector.Marked = !mark; + if (l.Back != null) l.Back.Sector.Marked = !mark; + } + } + } + + // Direct sectors + General.Map.Map.MarkSelectedSectors(true, mark); + + // Direct things + General.Map.Map.MarkSelectedThings(true, mark); + + // Sidedefs from linedefs or sectors + if (sidedefsfromsectors) + General.Map.Map.MarkSidedefsFromSectors(true, mark); + else + General.Map.Map.MarkSidedefsFromLinedefs(true, mark); + } + + #endregion + + #region ================== Indexing + + /// + /// Returns the vertex at the specified index. Returns null when index is out of range. This is an O(1) operation. + /// + public Vertex GetVertexByIndex(int index) + { + return index < numvertices ? vertices[index] : null; + } + + /// + /// Returns the linedef at the specified index. Returns null when index is out of range. This is an O(1) operation. + /// + public Linedef GetLinedefByIndex(int index) + { + return index < numlinedefs ? linedefs[index] : null; + } + + /// + /// Returns the sidedef at the specified index. Returns null when index is out of range. This is an O(1) operation. + /// + public Sidedef GetSidedefByIndex(int index) + { + return index < numsidedefs ? sidedefs[index] : null; + } + + /// + /// Returns the sector at the specified index. Returns null when index is out of range. This is an O(1) operation. + /// + public Sector GetSectorByIndex(int index) + { + return index < numsectors ? sectors[index] : null; + } + + /// + /// Returns the thing at the specified index. Returns null when index is out of range. This is an O(1) operation. + /// + public Thing GetThingByIndex(int index) + { + return index < numthings ? things[index] : null; + } + + #endregion + + #region ================== Areas + + /// This creates an initial, undefined area. + public static RectangleF CreateEmptyArea() + { + return new RectangleF(float.MaxValue / 2, float.MaxValue / 2, -float.MaxValue, -float.MaxValue); + } + + /// This creates an area from vertices. + public static RectangleF CreateArea(ICollection verts) + { + float l = float.MaxValue; + float t = float.MaxValue; + float r = float.MinValue; + float b = float.MinValue; + + // Go for all vertices + foreach (Vertex v in verts) + { + // Adjust boundaries by vertices + if (v.Position.x < l) l = v.Position.x; + if (v.Position.x > r) r = v.Position.x; + if (v.Position.y < t) t = v.Position.y; + if (v.Position.y > b) b = v.Position.y; + } + + // Return a rect + return new RectangleF(l, t, r - l, b - t); + } + + /// This increases and existing area with the given vertices. + public static RectangleF IncreaseArea(RectangleF area, ICollection verts) + { + float l = area.Left; + float t = area.Top; + float r = area.Right; + float b = area.Bottom; + + // Go for all vertices + foreach (Vertex v in verts) + { + // Adjust boundaries by vertices + if (v.Position.x < l) l = v.Position.x; + if (v.Position.x > r) r = v.Position.x; + if (v.Position.y < t) t = v.Position.y; + if (v.Position.y > b) b = v.Position.y; + } + + // Return a rect + return new RectangleF(l, t, r - l, b - t); + } + + /// This increases and existing area with the given things. + public static RectangleF IncreaseArea(RectangleF area, ICollection things) + { + float l = area.Left; + float t = area.Top; + float r = area.Right; + float b = area.Bottom; + + // Go for all vertices + foreach (Thing th in things) + { + // Adjust boundaries by vertices + if (th.Position.x < l) l = th.Position.x; + if (th.Position.x > r) r = th.Position.x; + if (th.Position.y < t) t = th.Position.y; + if (th.Position.y > b) b = th.Position.y; + } + + // Return a rect + return new RectangleF(l, t, r - l, b - t); + } + + /// This increases and existing area with the given vertices. + public static RectangleF IncreaseArea(RectangleF area, ICollection verts) + { + float l = area.Left; + float t = area.Top; + float r = area.Right; + float b = area.Bottom; + + // Go for all vertices + foreach (Vector2D v in verts) + { + // Adjust boundaries by vertices + if (v.x < l) l = v.x; + if (v.x > r) r = v.x; + if (v.y < t) t = v.y; + if (v.y > b) b = v.y; + } + + // Return a rect + return new RectangleF(l, t, r - l, b - t); + } + + /// This increases and existing area with the given vertex. + public static RectangleF IncreaseArea(RectangleF area, Vector2D vert) + { + float l = area.Left; + float t = area.Top; + float r = area.Right; + float b = area.Bottom; + + // Adjust boundaries by vertices + if (vert.x < l) l = vert.x; + if (vert.x > r) r = vert.x; + if (vert.y < t) t = vert.y; + if (vert.y > b) b = vert.y; + + // Return a rect + return new RectangleF(l, t, r - l, b - t); + } + + /// This creates an area from linedefs. + public static RectangleF CreateArea(ICollection lines) + { + float l = float.MaxValue; + float t = float.MaxValue; + float r = float.MinValue; + float b = float.MinValue; + + // Go for all linedefs + foreach (Linedef ld in lines) + { + // Adjust boundaries by vertices + if (ld.Start.Position.x < l) l = ld.Start.Position.x; + if (ld.Start.Position.x > r) r = ld.Start.Position.x; + if (ld.Start.Position.y < t) t = ld.Start.Position.y; + if (ld.Start.Position.y > b) b = ld.Start.Position.y; + if (ld.End.Position.x < l) l = ld.End.Position.x; + if (ld.End.Position.x > r) r = ld.End.Position.x; + if (ld.End.Position.y < t) t = ld.End.Position.y; + if (ld.End.Position.y > b) b = ld.End.Position.y; + } + + // Return a rect + return new RectangleF(l, t, r - l, b - t); + } + + /// This filters lines by a rectangular area. + public static ICollection FilterByArea(ICollection lines, ref RectangleF area) + { + ICollection newlines = new List(lines.Count); + + // Go for all lines + foreach (Linedef l in lines) + { + // Check the cs field bits + if ((GetCSFieldBits(l.Start, ref area) & GetCSFieldBits(l.End, ref area)) == 0) + { + // The line could be in the area + newlines.Add(l); + } + } + + // Return result + return newlines; + } + + // This returns the cohen-sutherland field bits for a vertex in a rectangle area + private static int GetCSFieldBits(Vertex v, ref RectangleF area) + { + int bits = 0; + if (v.Position.y < area.Top) bits |= 0x01; + if (v.Position.y > area.Bottom) bits |= 0x02; + if (v.Position.x < area.Left) bits |= 0x04; + if (v.Position.x > area.Right) bits |= 0x08; + return bits; + } + + /// This filters vertices by a rectangular area. + public static ICollection FilterByArea(ICollection verts, ref RectangleF area) + { + ICollection newverts = new List(verts.Count); + + // Go for all verts + foreach (Vertex v in verts) + { + // Within rect? + if ((v.Position.x >= area.Left) && + (v.Position.x <= area.Right) && + (v.Position.y >= area.Top) && + (v.Position.y <= area.Bottom)) + { + // The vertex is in the area + newverts.Add(v); + } + } + + // Return result + return newverts; + } + + #endregion + + #region ================== Stitching + + /// + /// Stitches marked geometry with non-marked geometry. Returns false when the operation failed. + /// + public bool StitchGeometry() + { + ICollection movinglines; + ICollection fixedlines; + ICollection nearbyfixedverts; + ICollection movingverts; + ICollection fixedverts; + RectangleF editarea; + int stitchundo; + + // Find vertices + movingverts = General.Map.Map.GetMarkedVertices(true); + fixedverts = General.Map.Map.GetMarkedVertices(false); + + // Find lines that moved during the drag + movinglines = LinedefsFromMarkedVertices(false, true, true); + + // Find all non-moving lines + fixedlines = LinedefsFromMarkedVertices(true, false, false); + + // Determine area in which we are editing + editarea = MapSet.CreateArea(movinglines); + editarea = MapSet.IncreaseArea(editarea, movingverts); + editarea.Inflate(1.0f, 1.0f); + + // Join nearby vertices + BeginAddRemove(); + MapSet.JoinVertices(fixedverts, movingverts, true, MapSet.STITCH_DISTANCE); + EndAddRemove(); + + // Update cached values of lines because we need their length/angle + Update(true, false); + + BeginAddRemove(); + + // Split moving lines with unselected vertices + nearbyfixedverts = MapSet.FilterByArea(fixedverts, ref editarea); + if (!MapSet.SplitLinesByVertices(movinglines, nearbyfixedverts, MapSet.STITCH_DISTANCE, movinglines)) + return false; + + // Split non-moving lines with selected vertices + fixedlines = MapSet.FilterByArea(fixedlines, ref editarea); + if (!MapSet.SplitLinesByVertices(fixedlines, movingverts, MapSet.STITCH_DISTANCE, movinglines)) + return false; + + // Remove looped linedefs + MapSet.RemoveLoopedLinedefs(movinglines); + + // Join overlapping lines + if (!MapSet.JoinOverlappingLines(movinglines)) + return false; + + EndAddRemove(); + + return true; + } + + #endregion + + #region ================== Geometry Tools + + /// This removes any virtual sectors in the map and returns the number of sectors removed. + public int RemoveVirtualSectors() + { + int count = 0; + int index = 0; + + // Go for all sectors + while (index < numsectors) + { + // Remove when virtual + if (sectors[index].Fields.ContainsKey(VIRTUAL_SECTOR_FIELD)) + { + sectors[index].Dispose(); + count++; + } + else + { + index++; + } + } + + return count; + } + + /// This removes unused sectors and returns the number of removed sectors. + public int RemoveUnusedSectors(bool reportwarnings) + { + int count = 0; + int index = numsectors - 1; + + // Go for all sectors + while (index >= 0) + { + // Remove when unused + if (sectors[index].Sidedefs.Count == 0) + { + if (reportwarnings) + General.ErrorLogger.Add(ErrorType.Warning, "Sector " + index + " was unused and has been removed."); + + sectors[index].Dispose(); + count++; + } + + index--; + } + + return count; + } + + /// This joins overlapping lines together. Returns false when the operation failed. + public static bool JoinOverlappingLines(ICollection lines) + { + bool joined; + + do + { + // No joins yet + joined = false; + + // Go for all the lines + foreach (Linedef l1 in lines) + { + // Check if these vertices have lines that overlap + foreach (Linedef l2 in l1.Start.Linedefs) + { + // Sharing vertices? + if ((l1.End == l2.End) || + (l1.End == l2.Start)) + { + // Not the same line? + if (l1 != l2) + { + bool oppositedirection = (l1.End == l2.Start); + bool l2marked = l2.Marked; + + // Merge these two linedefs + while (lines.Remove(l2)) ; + if (!l2.Join(l1)) return false; + + // If l2 was marked as new geometry, we have to make sure + // that l1's FrontInterior is correct for the drawing procedure + if (l2marked) + { + l1.FrontInterior = l2.FrontInterior ^ oppositedirection; + } + // If l1 is marked as new geometry, we may need to flip it to preserve + // orientation of the original geometry, and update its FrontInterior + else if (l1.Marked) + { + if (oppositedirection) + { + l1.FlipVertices(); // This also flips FrontInterior + l1.FlipSidedefs(); + } + } + + joined = true; + break; + } + } + } + + // Will have to restart when joined + if (joined) break; + + // Check if these vertices have lines that overlap + foreach (Linedef l2 in l1.End.Linedefs) + { + // Sharing vertices? + if ((l1.Start == l2.End) || + (l1.Start == l2.Start)) + { + // Not the same line? + if (l1 != l2) + { + bool oppositedirection = (l1.Start == l2.End); + bool l2marked = l2.Marked; + + // Merge these two linedefs + while (lines.Remove(l2)) ; + if (!l2.Join(l1)) return false; + + // If l2 was marked as new geometry, we have to make sure + // that l1's FrontInterior is correct for the drawing procedure + if (l2marked) + { + l1.FrontInterior = l2.FrontInterior ^ oppositedirection; + } + // If l1 is marked as new geometry, we may need to flip it to preserve + // orientation of the original geometry, and update its FrontInterior + else if (l1.Marked) + { + if (oppositedirection) + { + l1.FlipVertices(); // This also flips FrontInterior + l1.FlipSidedefs(); + } + } + + joined = true; + break; + } + } + } + + // Will have to restart when joined + if (joined) break; + } + } + while (joined); + + // Return result + return true; + } + + /// This removes looped linedefs (linedefs which reference the same vertex for + /// start and end) and returns the number of linedefs removed. + public static int RemoveLoopedLinedefs(ICollection lines) + { + int linesremoved = 0; + bool removedline; + + do + { + // Nothing removed yet + removedline = false; + + // Go for all the lines + foreach (Linedef l in lines) + { + // Check if referencing the same vertex twice + if (l.Start == l.End) + { + // Remove this line + while (lines.Remove(l)) ; + l.Dispose(); + linesremoved++; + removedline = true; + break; + } + } + } + while (removedline); + + // Return result + return linesremoved; + } + + /// This joins nearby vertices from two collections. This does NOT join vertices + /// within the same collection, only if they exist in both collections. + /// The vertex from the second collection is moved to match the first vertex. + /// When keepsecond is true, the vertex in the second collection is kept, + /// otherwise the vertex in the first collection is kept. + /// Returns the number of joins made. + public static int JoinVertices(ICollection set1, ICollection set2, bool keepsecond, float joindist) + { + float joindist2 = joindist * joindist; + int joinsdone = 0; + bool joined; + + do + { + // No joins yet + joined = false; + + // Go for all vertices in the first set + foreach (Vertex v1 in set1) + { + // Go for all vertices in the second set + foreach (Vertex v2 in set2) + { + // Check if vertices are close enough + if (v1.DistanceToSq(v2.Position) <= joindist2) + { + // Check if not the same vertex + if (v1 != v2) + { + // Move the second vertex to match the first + v2.Move(v1.Position); + + // Check which one to keep + if (keepsecond) + { + // Join the first into the second + // Second is kept, first is removed + v1.Join(v2); + set1.Remove(v1); + set2.Remove(v1); + } + else + { + // Join the second into the first + // First is kept, second is removed + v2.Join(v1); + set1.Remove(v2); + set2.Remove(v2); + } + + // Count the join + joinsdone++; + joined = true; + break; + } + } + } + + // Will have to restart when joined + if (joined) break; + } + } + while (joined); + + // Return result + return joinsdone; + } + + /// This corrects lines that have a back sidedef but no front sidedef by flipping them. Returns the number of flips made. + public static int FlipBackwardLinedefs(ICollection lines) + { + int flipsdone = 0; + + // Examine all lines + foreach (Linedef l in lines) + { + // Back side but no front side? + if ((l.Back != null) && (l.Front == null)) + { + // Flip that linedef! + l.FlipVertices(); + l.FlipSidedefs(); + flipsdone++; + } + } + + // Return result + return flipsdone; + } + + /// This splits the given lines with the given vertices. All affected lines + /// will be added to changedlines. Returns false when the operation failed. + public static bool SplitLinesByVertices(ICollection lines, ICollection verts, float splitdist, ICollection changedlines) + { + float splitdist2 = splitdist * splitdist; + bool splitted; + + do + { + // No split yet + splitted = false; + + // Go for all the lines + foreach (Linedef l in lines) + { + // Go for all the vertices + foreach (Vertex v in verts) + { + // Check if v is close enough to l for splitting + if (l.DistanceToSq(v.Position, true) <= splitdist2) + { + // Line is not already referencing v? + Vector2D deltastart = l.Start.Position - v.Position; + Vector2D deltaend = l.End.Position - v.Position; + if (((Math.Abs(deltastart.x) > 0.001f) || + (Math.Abs(deltastart.y) > 0.001f)) && + ((Math.Abs(deltaend.x) > 0.001f) || + (Math.Abs(deltaend.y) > 0.001f))) + { + // Split line l with vertex v + Linedef nl = l.Split(v); + if (nl == null) return false; + + // Add the new line to the list + lines.Add(nl); + + // Both lines must be updated because their new length + // is relevant for next iterations! + l.UpdateCache(); + nl.UpdateCache(); + + // Add both lines to changedlines + if (changedlines != null) + { + changedlines.Add(l); + changedlines.Add(nl); + } + + // Count the split + splitted = true; + break; + } + } + } + + // Will have to restart when splitted + // TODO: If we make (linked) lists from the collections first, + // we don't have to restart when splitted? + if (splitted) break; + } + } + while (splitted); + + return true; + } + + /// This finds the side closest to the specified position. + public static Sidedef NearestSidedef(ICollection selection, Vector2D pos) + { + Sidedef closest = null; + float distance = float.MaxValue; + + // Go for all sidedefs in selection + foreach (Sidedef sd in selection) + { + // Calculate distance and check if closer than previous find + float d = sd.Line.SafeDistanceToSq(pos, true); + if (d == distance) + { + // Same distance, so only pick the one that is on the right side of the line + float side = sd.Line.SideOfLine(pos); + if (((side <= 0.0f) && sd.IsFront) || ((side > 0.0f) && !sd.IsFront)) + { + closest = sd; + distance = d; + } + } + else if (d < distance) + { + // This one is closer + closest = sd; + distance = d; + } + } + + // Return result + return closest; + } + + /// This finds the line closest to the specified position. + public static Linedef NearestLinedef(ICollection selection, Vector2D pos) + { + Linedef closest = null; + float distance = float.MaxValue; + + // Go for all linedefs in selection + foreach (Linedef l in selection) + { + // Calculate distance and check if closer than previous find + float d = l.SafeDistanceToSq(pos, true); + if (d < distance) + { + // This one is closer + closest = l; + distance = d; + } + } + + // Return result + return closest; + } + + /// This finds the line closest to the specified position. + public static Linedef NearestLinedefRange(ICollection selection, Vector2D pos, float maxrange) + { + Linedef closest = null; + float distance = float.MaxValue; + float maxrangesq = maxrange * maxrange; + float d; + + // Go for all linedefs in selection + foreach (Linedef l in selection) + { + // Calculate distance and check if closer than previous find + d = l.SafeDistanceToSq(pos, true); + if ((d <= maxrangesq) && (d < distance)) + { + // This one is closer + closest = l; + distance = d; + } + } + + // Return result + return closest; + } + + /// This finds the vertex closest to the specified position. + public static Vertex NearestVertex(ICollection selection, Vector2D pos) + { + Vertex closest = null; + float distance = float.MaxValue; + float d; + + // Go for all vertices in selection + foreach (Vertex v in selection) + { + // Calculate distance and check if closer than previous find + d = v.DistanceToSq(pos); + if (d < distance) + { + // This one is closer + closest = v; + distance = d; + } + } + + // Return result + return closest; + } + + /// This finds the thing closest to the specified position. + public static Thing NearestThing(ICollection selection, Vector2D pos) + { + Thing closest = null; + float distance = float.MaxValue; + float d; + + // Go for all things in selection + foreach (Thing t in selection) + { + // Calculate distance and check if closer than previous find + d = t.DistanceToSq(pos); + if (d < distance) + { + // This one is closer + closest = t; + distance = d; + } + } + + // Return result + return closest; + } + + /// This finds the vertex closest to the specified position. + public static Vertex NearestVertexSquareRange(ICollection selection, Vector2D pos, float maxrange) + { + RectangleF range = RectangleF.FromLTRB(pos.x - maxrange, pos.y - maxrange, pos.x + maxrange, pos.y + maxrange); + Vertex closest = null; + float distance = float.MaxValue; + float d; + + // Go for all vertices in selection + foreach (Vertex v in selection) + { + // Within range? + if ((v.Position.x >= range.Left) && (v.Position.x <= range.Right)) + { + if ((v.Position.y >= range.Top) && (v.Position.y <= range.Bottom)) + { + // Close than previous find? + d = Math.Abs(v.Position.x - pos.x) + Math.Abs(v.Position.y - pos.y); + if (d < distance) + { + // This one is closer + closest = v; + distance = d; + } + } + } + } + + // Return result + return closest; + } + + /// This finds the thing closest to the specified position. + public static Thing NearestThingSquareRange(ICollection selection, Vector2D pos, float maxrange) + { + RectangleF range = RectangleF.FromLTRB(pos.x - maxrange, pos.y - maxrange, pos.x + maxrange, pos.y + maxrange); + Thing closest = null; + float distance = float.MaxValue; + float d; + + // Go for all vertices in selection + foreach (Thing t in selection) + { + // Within range? + if ((t.Position.x >= (range.Left - t.Size)) && (t.Position.x <= (range.Right + t.Size))) + { + if ((t.Position.y >= (range.Top - t.Size)) && (t.Position.y <= (range.Bottom + t.Size))) + { + // Close than previous find? + d = Math.Abs(t.Position.x - pos.x) + Math.Abs(t.Position.y - pos.y); + if (d < distance) + { + // This one is closer + closest = t; + distance = d; + } + } + } + } + + // Return result + return closest; + } + + #endregion + + #region ================== Tools + + /// This snaps all vertices to the map format accuracy. Call this to ensure the vertices are at valid coordinates. + public void SnapAllToAccuracy() + { + foreach (Vertex v in vertices) v.SnapToAccuracy(); + foreach (Thing t in things) t.SnapToAccuracy(); + } + + /// This returns the next unused tag number. + public int GetNewTag() + { + Dictionary usedtags = new Dictionary(); + ForAllTags(NewTagHandler, false, usedtags); + ForAllTags(NewTagHandler, true, usedtags); + + // Now find the first unused index + for (int i = 1; i <= General.Map.FormatInterface.MaxTag; i++) + if (!usedtags.ContainsKey(i)) return i; + + // All tags used! + return 0; + } + + /// This returns the next unused tag number within the marked geometry. + public int GetNewTag(bool marked) + { + Dictionary usedtags = new Dictionary(); + ForAllTags(NewTagHandler, marked, usedtags); + + // Now find the first unused index + for (int i = 1; i <= General.Map.FormatInterface.MaxTag; i++) + if (!usedtags.ContainsKey(i)) return i; + + // All tags used! + return 0; + } + + /// This returns the next unused tag number. + public List GetMultipleNewTags(int count) + { + List newtags = new List(count); + if (count > 0) + { + Dictionary usedtags = new Dictionary(); + ForAllTags(NewTagHandler, false, usedtags); + ForAllTags(NewTagHandler, true, usedtags); + + // Find unused tags and add them + for (int i = 1; i <= General.Map.FormatInterface.MaxTag; i++) + { + if (!usedtags.ContainsKey(i)) + { + newtags.Add(i); + if (newtags.Count == count) break; + } + } + } + + return newtags; + } + + /// This returns the next unused tag number within the marked geometry. + public List GetMultipleNewTags(int count, bool marked) + { + List newtags = new List(count); + if (count > 0) + { + Dictionary usedtags = new Dictionary(); + ForAllTags(NewTagHandler, marked, usedtags); + + // Find unused tags and add them + for (int i = 1; i <= General.Map.FormatInterface.MaxTag; i++) + { + if (!usedtags.ContainsKey(i)) + { + newtags.Add(i); + if (newtags.Count == count) break; + } + } + } + + return newtags; + } + + // Handler for finding a new tag + private void NewTagHandler(MapElement element, bool actionargument, UniversalType type, ref int value, Dictionary usedtags) + { + usedtags[value] = true; + } + + /// This calls a function for all tag fields in the marked or unmarked geometry. The obj parameter can be anything you wish to pass on to your TagHandler function. + public void ForAllTags(TagHandler handler, bool marked, T obj) + { + // Remove tags from sectors + foreach (Sector s in sectors) + if (s.Marked == marked) + { + int tag = s.Tag; + handler(s, false, UniversalType.SectorTag, ref tag, obj); + if (tag != s.Tag) s.Tag = tag; + } + + // Remove tags from things + if (General.Map.FormatInterface.HasThingTag) + { + foreach (Thing t in things) + if (t.Marked == marked) + { + int tag = t.Tag; + handler(t, false, UniversalType.ThingTag, ref tag, obj); + if (tag != t.Tag) t.Tag = tag; + } + } + + // Remove tags from thing actions + if (General.Map.FormatInterface.HasThingAction && + General.Map.FormatInterface.HasActionArgs) + { + foreach (Thing t in things) + { + if (t.Marked == marked) + { + LinedefActionInfo info = General.Map.Config.GetLinedefActionInfo(t.Action); + for (int i = 0; i < Thing.NUM_ARGS; i++) + if (info.Args[i].Used && CheckIsTagType(info.Args[i].Type)) + { + int tag = t.Args[i]; + handler(t, true, (UniversalType)(info.Args[i].Type), ref tag, obj); + if (tag != t.Args[i]) t.Args[i] = tag; + } + } + } + } + + // Remove tags from linedefs + if (General.Map.FormatInterface.HasLinedefTag) + { + foreach (Linedef l in linedefs) + if (l.Marked == marked) + { + int tag = l.Tag; + handler(l, false, UniversalType.LinedefTag, ref tag, obj); + if (tag != l.Tag) l.Tag = tag; + } + } + + // Remove tags from linedef actions + if (General.Map.FormatInterface.HasActionArgs) + { + foreach (Linedef l in linedefs) + { + if (l.Marked == marked) + { + LinedefActionInfo info = General.Map.Config.GetLinedefActionInfo(l.Action); + for (int i = 0; i < Linedef.NUM_ARGS; i++) + if (info.Args[i].Used && CheckIsTagType(info.Args[i].Type)) + { + int tag = l.Args[i]; + handler(l, true, (UniversalType)(info.Args[i].Type), ref tag, obj); + if (tag != l.Args[i]) l.Args[i] = tag; + } + } + } + } + } + + // This checks if the given action argument type is a tag type + private bool CheckIsTagType(int argtype) + { + return (argtype == (int)UniversalType.LinedefTag) || + (argtype == (int)UniversalType.SectorTag) || + (argtype == (int)UniversalType.ThingTag); + } + + /// This makes a list of lines related to marked vertices. + /// A line is unstable when one vertex is marked and the other isn't. + public ICollection LinedefsFromMarkedVertices(bool includeunselected, bool includestable, bool includeunstable) + { + List list = new List((numlinedefs / 2) + 1); + + // Go for all lines + foreach (Linedef l in linedefs) + { + // Check if this is to be included + if ((includestable && (l.Start.Marked && l.End.Marked)) || + (includeunstable && (l.Start.Marked ^ l.End.Marked)) || + (includeunselected && (!l.Start.Marked && !l.End.Marked))) + { + // Add to list + list.Add(l); + } + } + + // Return result + return list; + } + + /// This makes a list of unstable lines from the given vertices. + /// A line is unstable when one vertex is selected and the other isn't. + public static ICollection UnstableLinedefsFromVertices(ICollection verts) + { + Dictionary lines = new Dictionary(); + + // Go for all vertices + foreach (Vertex v in verts) + { + // Go for all lines + foreach (Linedef l in v.Linedefs) + { + // If the line exists in the list + if (lines.ContainsKey(l)) + { + // Remove it + lines.Remove(l); + } + // Otherwise add it + else + { + // Add the line + lines.Add(l, l); + } + } + } + + // Return result + return new List(lines.Values); + } + + /// This finds the line closest to the specified position. + public Linedef NearestLinedef(Vector2D pos) { return MapSet.NearestLinedef(linedefs, pos); } + + /// This finds the line closest to the specified position. + public Linedef NearestLinedefRange(Vector2D pos, float maxrange) { return MapSet.NearestLinedefRange(linedefs, pos, maxrange); } + + /// This finds the vertex closest to the specified position. + public Vertex NearestVertex(Vector2D pos) { return MapSet.NearestVertex(vertices, pos); } + + /// This finds the vertex closest to the specified position. + public Vertex NearestVertexSquareRange(Vector2D pos, float maxrange) { return MapSet.NearestVertexSquareRange(vertices, pos, maxrange); } + + /// This finds the thing closest to the specified position. + public Thing NearestThingSquareRange(Vector2D pos, float maxrange) { return MapSet.NearestThingSquareRange(things, pos, maxrange); } + + /// This finds the closest unselected linedef that is not connected to the given vertex. + public Linedef NearestUnselectedUnreferencedLinedef(Vector2D pos, float maxrange, Vertex v, out float distance) + { + Linedef closest = null; + distance = float.MaxValue; + float maxrangesq = maxrange * maxrange; + float d; + + // Go for all linedefs in selection + foreach (Linedef l in linedefs) + { + // Calculate distance and check if closer than previous find + d = l.SafeDistanceToSq(pos, true); + if ((d <= maxrangesq) && (d < distance)) + { + // Check if not selected + + // Check if linedef is not connected to v + if ((l.Start != v) && (l.End != v)) + { + // This one is closer + closest = l; + distance = d; + } + } + } + + // Return result + return closest; + } + + // This performs sidedefs compression + // Note: Only use this for saving, because this messes up the expected data structure horribly. + internal void CompressSidedefs() + { + Dictionary> storedsides = new Dictionary>(numsidedefs); + int originalsidescount = numsidedefs; + double starttime = General.stopwatch.Elapsed.TotalMilliseconds; + + BeginAddRemove(); + + int sn = 0; + while (sn < numsidedefs) + { + Sidedef stored = null; + Sidedef snsd = sidedefs[sn]; + + // Check if checksum is stored + bool samesidedef = false; + uint checksum = snsd.GetChecksum(); + bool checksumstored = storedsides.ContainsKey(checksum); + if (checksumstored) + { + List othersides = storedsides[checksum]; + foreach (Sidedef os in othersides) + { + // They must be in the same sector + if (snsd.Sector == os.Sector) + { + // Check if sidedefs are really the same + stored = os; + MemoryStream sidemem = new MemoryStream(1024); + SerializerStream sidedata = new SerializerStream(sidemem); + MemoryStream othermem = new MemoryStream(1024); + SerializerStream otherdata = new SerializerStream(othermem); + snsd.ReadWrite(sidedata); + os.ReadWrite(otherdata); + if (sidemem.Length == othermem.Length) + { + samesidedef = true; + sidemem.Seek(0, SeekOrigin.Begin); + othermem.Seek(0, SeekOrigin.Begin); + for (int i = 0; i < sidemem.Length; i++) + { + if (sidemem.ReadByte() != othermem.ReadByte()) + { + samesidedef = false; + break; + } + } + } + + if (samesidedef) break; + } + } + } + + // Same sidedef? + if (samesidedef) + { + // Replace with stored sidedef + bool isfront = snsd.IsFront; + Linedef ld = snsd.Line; + snsd.Line.DetachSidedefP(snsd); + if (isfront) + ld.AttachFront(stored); + else + ld.AttachBack(stored); + + // Remove the sidedef + snsd.SetSector(null); + RemoveSidedef(sn); + } + else + { + // Store this new one + if (checksumstored) + { + storedsides[checksum].Add(snsd); + } + else + { + List newlist = new List(4); + newlist.Add(snsd); + storedsides.Add(checksum, newlist); + } + + // Next + sn++; + } + } + + EndAddRemove(); + + // Output info + double endtime = General.stopwatch.ElapsedMilliseconds; + double deltatimesec = (endtime - starttime) / 1000.0d; + float ratio = 100.0f - (((float)numsidedefs / (float)originalsidescount) * 100.0f); + General.WriteLogLine("Sidedefs compressed: " + numsidedefs + " remaining out of " + originalsidescount + " (" + ratio.ToString("########0.00") + "%) in " + deltatimesec.ToString("########0.00") + " seconds"); + } + + // This converts flags and activations to UDMF fields + internal void TranslateToUDMF() + { + // FUCK UDMF + //foreach(Linedef l in linedefs) l.TranslateToUDMF(); + //foreach(Thing t in things) t.TranslateToUDMF(); + } + + // This converts UDMF fields back into flags and activations + // NOTE: Only converts the marked items + internal void TranslateFromUDMF() + { + // FUCK UDMF + //foreach(Linedef l in linedefs) if(l.Marked) l.TranslateFromUDMF(); + //foreach(Thing t in things) if(t.Marked) t.TranslateFromUDMF(); + } + + /// This removes unused vertices. + public void RemoveUnusedVertices() + { + // Go for all vertices + int index = numvertices - 1; + while (index >= 0) + { + if ((vertices[index] != null) && (vertices[index].Linedefs.Count == 0)) + vertices[index].Dispose(); + else + index--; + } + } + + public Sector GetSectorByCoordinates (Vector2D pos, VisualBlockMap blockmap) + { + return blockmap.GetSectorAt(pos); + } + + #endregion + } +} diff --git a/Source/Core/Map/Sector.cs b/Source/Core/Map/Sector.cs new file mode 100644 index 0000000..f0e4c25 --- /dev/null +++ b/Source/Core/Map/Sector.cs @@ -0,0 +1,744 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Geometry; +using System.Drawing; +using CodeImp.DoomBuilder.Rendering; +using System.Collections.ObjectModel; +using SlimDX.Direct3D9; +using SlimDX; + +#endregion + +namespace CodeImp.DoomBuilder.Map +{ + public sealed class Sector : SelectableElement + { + #region ================== Constants + + public const int NUM_COLORS = 5; // villsa 9/14/11 (builder64) + + #endregion + + #region ================== Variables + + // Map + private MapSet map; + + // List items + private LinkedListNode selecteditem; + + // Sidedefs + private LinkedList sidedefs; + + // Properties + private int fixedindex; + private int floorheight; + private int ceilheight; + private string floortexname; + private string ceiltexname; + private long longfloortexname; + private long longceiltexname; + private int effect; + private int tag; + private int brightness; + private Dictionary flags; // villsa + private Lights ceilColor; // villsa + private Lights flrColor; // villsa + private Lights thingColor; // villsa + private Lights topColor; // villsa + private Lights lwrColor; // villsa + private uint hashfloortexname; // villsa + private uint hashceilingtexname; // villsa + + // Cloning + private Sector clone; + private int serializedindex; + + // Triangulation + private bool updateneeded; + private bool triangulationneeded; + private RectangleF bbox; + private Triangulation triangles; + private FlatVertex[] flatvertices; + private ReadOnlyCollection labels; + private SurfaceEntry surfaceentry; + + #endregion + + #region ================== Properties + + public MapSet Map { get { return map; } } + public ICollection Sidedefs { get { return sidedefs; } } + + /// + /// An unique index that does not change when other sectors are removed. + /// + public int FixedIndex { get { return fixedindex; } } + public int FloorHeight { get { return floorheight; } set { BeforePropsChange(); floorheight = value; } } + public int CeilHeight { get { return ceilheight; } set { BeforePropsChange(); ceilheight = value; } } + public string FloorTexture { get { return floortexname; } } + public string CeilTexture { get { return ceiltexname; } } + + // villsa + public uint HashFloor { get { return hashfloortexname; } set { hashfloortexname = value; } } + public uint HashCeiling { get { return hashceilingtexname; } set { hashceilingtexname = value; } } + + public long LongFloorTexture { get { return longfloortexname; } } + public long LongCeilTexture { get { return longceiltexname; } } + public int Effect { get { return effect; } set { BeforePropsChange(); effect = value; } } + public int Tag { get { return tag; } set { BeforePropsChange(); tag = value; if ((tag < General.Map.FormatInterface.MinTag) || (tag > General.Map.FormatInterface.MaxTag)) throw new ArgumentOutOfRangeException("Tag", "Invalid tag number"); } } + public int Brightness { get { return brightness; } set { BeforePropsChange(); brightness = value; updateneeded = true; } } + public bool UpdateNeeded { get { return updateneeded; } set { updateneeded |= value; triangulationneeded |= value; } } + public RectangleF BBox { get { return bbox; } } + internal Sector Clone { get { return clone; } set { clone = value; } } + internal int SerializedIndex { get { return serializedindex; } set { serializedindex = value; } } + public Triangulation Triangles { get { return triangles; } } + public FlatVertex[] FlatVertices { get { return flatvertices; } } + public ReadOnlyCollection Labels { get { return labels; } } + internal Dictionary Flags { get { return flags; } } // villsa + public Lights CeilColor { get { return ceilColor; } set { BeforePropsChange(); ceilColor = value; } } // villsa + public Lights FloorColor { get { return flrColor; } set { BeforePropsChange(); flrColor = value; } } // villsa + public Lights ThingColor { get { return thingColor; } set { BeforePropsChange(); thingColor = value; } } // villsa + public Lights TopColor { get { return topColor; } set { BeforePropsChange(); topColor = value; } } // villsa + public Lights LowerColor { get { return lwrColor; } set { BeforePropsChange(); lwrColor = value; } } // villsa + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + internal Sector(MapSet map, int listindex, int index) + { + // Initialize + this.map = map; + this.listindex = listindex; + this.sidedefs = new LinkedList(); + this.fixedindex = index; + this.floortexname = "-"; + this.ceiltexname = "-"; + this.longfloortexname = MapSet.EmptyLongName; + this.longceiltexname = MapSet.EmptyLongName; + this.updateneeded = true; + this.triangulationneeded = true; + this.surfaceentry = new SurfaceEntry(-1, -1, -1); + this.flags = new Dictionary(); // villsa + this.ceilColor = new Lights(128, 128, 128, 0); // villsa + this.flrColor = new Lights(128, 128, 128, 0); // villsa + this.thingColor = new Lights(128, 128, 128, 0); // villsa + this.topColor = new Lights(128, 128, 128, 0); // villsa + this.lwrColor = new Lights(128, 128, 128, 0); // villsa + + if (map == General.Map.Map) + General.Map.UndoRedo.RecAddSector(this); + + // We have no destructor + GC.SuppressFinalize(this); + } + + // Disposer + public override void Dispose() + { + // Not already disposed? + if (!isdisposed) + { + // Already set isdisposed so that changes can be prohibited + isdisposed = true; + + // Dispose the sidedefs that are attached to this sector + // because a sidedef cannot exist without reference to its sector. + if (map.AutoRemove) + foreach (Sidedef sd in sidedefs) sd.Dispose(); + else + foreach (Sidedef sd in sidedefs) sd.SetSectorP(null); + + if (map == General.Map.Map) + General.Map.UndoRedo.RecRemSector(this); + + // Remove from main list + map.RemoveSector(listindex); + + // Register the index as free + map.AddSectorIndexHole(fixedindex); + + // Free surface entry + General.Map.CRenderer2D.Surfaces.FreeSurfaces(surfaceentry); + + // Clean up + sidedefs = null; + map = null; + + // Dispose base + base.Dispose(); + } + } + + #endregion + + #region ================== Management + + // Call this before changing properties + protected override void BeforePropsChange() + { + if (map == General.Map.Map) + General.Map.UndoRedo.RecPrpSector(this); + } + + // Serialize / deserialize (passive: this doesn't record) + internal void ReadWrite(IReadWriteStream s) + { + if (!s.IsWriting) + { + BeforePropsChange(); + updateneeded = true; + } + + base.ReadWrite(s); + + // villsa + if (s.IsWriting) + { + s.wInt(flags.Count); + + foreach (KeyValuePair f in flags) + { + s.wString(f.Key); + s.wBool(f.Value); + } + } + else + { + int c; s.rInt(out c); + + flags = new Dictionary(c); + for (int i = 0; i < c; i++) + { + string t; s.rString(out t); + bool b; s.rBool(out b); + flags.Add(t, b); + } + } + + s.rwInt(ref fixedindex); + s.rwInt(ref floorheight); + s.rwInt(ref ceilheight); + s.rwString(ref floortexname); + s.rwString(ref ceiltexname); + s.rwLong(ref longfloortexname); + s.rwLong(ref longceiltexname); + s.rwInt(ref effect); + s.rwInt(ref tag); + s.rwInt(ref brightness); + // villsa + if (General.Map.FormatInterface.InDoom64Mode) + { + s.rwLight(ref ceilColor); + s.rwLight(ref flrColor); + s.rwLight(ref thingColor); + s.rwLight(ref topColor); + s.rwLight(ref lwrColor); + } + } + + // After deserialization + internal void PostDeserialize(MapSet map) + { + triangles.PostDeserialize(map); + updateneeded = true; + triangulationneeded = true; + } + + // This copies all properties to another sector + public void CopyPropertiesTo(Sector s) + { + s.BeforePropsChange(); + + // Copy properties + s.ceilheight = ceilheight; + s.ceiltexname = ceiltexname; + s.longceiltexname = longceiltexname; + s.floorheight = floorheight; + s.floortexname = floortexname; + s.longfloortexname = longfloortexname; + s.effect = effect; + s.tag = tag; + s.flags = new Dictionary(flags); // villsa + s.brightness = brightness; + s.updateneeded = true; + s.ceilColor = ceilColor; // villsa + s.flrColor = flrColor; // villsa + s.thingColor = thingColor; // villsa + s.topColor = topColor; // villsa + s.lwrColor = lwrColor; // villsa + base.CopyPropertiesTo(s); + } + + // This attaches a sidedef and returns the listitem + internal LinkedListNode AttachSidedefP(Sidedef sd) + { + updateneeded = true; + triangulationneeded = true; + return sidedefs.AddLast(sd); + } + + // This detaches a sidedef + internal void DetachSidedefP(LinkedListNode l) + { + // Not disposing? + if (!isdisposed) + { + // Remove sidedef + updateneeded = true; + triangulationneeded = true; + sidedefs.Remove(l); + + // No more sidedefs left? + if ((sidedefs.Count == 0) && map.AutoRemove) + { + // This sector is now useless, dispose it + this.Dispose(); + } + } + } + + // This triangulates the sector geometry + internal void Triangulate() + { + if (updateneeded) + { + // Triangulate again? + if (triangulationneeded || (triangles == null)) + { + // Triangulate sector + triangles = Triangulation.Create(this); + triangulationneeded = false; + updateneeded = true; + + // Make label positions + labels = Array.AsReadOnly(Tools.FindLabelPositions(this).ToArray()); + + // Number of vertices changed? + if ((surfaceentry != null) && (triangles.Vertices.Count != surfaceentry.numvertices)) + General.Map.CRenderer2D.Surfaces.FreeSurfaces(surfaceentry); + } + } + } + + // This makes new vertices as well as floor and ceiling surfaces + internal void CreateSurfaces() + { + if (updateneeded) + { + // Brightness color + int brightint; + + // villsa + switch (General.Map.Renderer2D.ViewMode) + { + case ViewMode.FloorColor: + brightint = this.flrColor.color.ToInt(); + break; + case ViewMode.CeilingColor: + brightint = this.ceilColor.color.ToInt(); + break; + case ViewMode.ThingColor: + brightint = this.thingColor.color.ToInt(); + break; + case ViewMode.FloorTextures: + if (Renderer.FullBrightness) + { + brightint = General.Map.Renderer2D.CalculateBrightness(brightness); + } + else + { + brightint = this.flrColor.color.ToInt(); + } + break; + case ViewMode.CeilingTextures: + if (Renderer.FullBrightness) + { + brightint = General.Map.Renderer2D.CalculateBrightness(brightness); + } + else + { + brightint = this.ceilColor.color.ToInt(); + } + break; + default: + brightint = this.flrColor.color.ToInt(); + break; + } + + // Make vertices + flatvertices = new FlatVertex[triangles.Vertices.Count]; + for (int i = 0; i < triangles.Vertices.Count; i++) + { + flatvertices[i].x = triangles.Vertices[i].x; + flatvertices[i].y = triangles.Vertices[i].y; + flatvertices[i].z = 1.0f; + flatvertices[i].c = brightint; + flatvertices[i].u = triangles.Vertices[i].x; + flatvertices[i].v = triangles.Vertices[i].y; + } + + // Create bounding box + bbox = CreateBBox(); + + // Make a dummy entry if we don't have one yet + if (surfaceentry == null) surfaceentry = new SurfaceEntry(-1, -1, -1); + + // Create floor vertices + FlatVertex[] floorvertices = new FlatVertex[flatvertices.Length]; + flatvertices.CopyTo(floorvertices, 0); + General.Plugins.OnSectorFloorSurfaceUpdate(this, ref floorvertices); + surfaceentry.floorvertices = floorvertices; + surfaceentry.floortexture = longfloortexname; + + // Create ceiling vertices + FlatVertex[] ceilvertices = new FlatVertex[flatvertices.Length]; + flatvertices.CopyTo(ceilvertices, 0); + General.Plugins.OnSectorCeilingSurfaceUpdate(this, ref ceilvertices); + surfaceentry.ceilvertices = ceilvertices; + surfaceentry.ceiltexture = longceiltexname; + + // Update entry + surfaceentry = General.Map.CRenderer2D.Surfaces.UpdateSurfaces(surfaceentry); + + // Updated + updateneeded = false; + } + } + + // This updates the floor surface + public void UpdateFloorSurface() + { + if (flatvertices == null) return; + + // Create floor vertices + FlatVertex[] floorvertices = new FlatVertex[flatvertices.Length]; + flatvertices.CopyTo(floorvertices, 0); + General.Plugins.OnSectorFloorSurfaceUpdate(this, ref floorvertices); + surfaceentry.floorvertices = floorvertices; + surfaceentry.floortexture = longfloortexname; + if (surfaceentry.ceilvertices == null) + surfaceentry.ceilvertices = floorvertices; + + // Update entry + surfaceentry = General.Map.CRenderer2D.Surfaces.UpdateSurfaces(surfaceentry); + General.Map.CRenderer2D.Surfaces.UnlockBuffers(); + } + + // This updates the ceiling surface + public void UpdateCeilingSurface() + { + if (flatvertices == null) return; + + // Create ceiling vertices + FlatVertex[] ceilvertices = new FlatVertex[flatvertices.Length]; + flatvertices.CopyTo(ceilvertices, 0); + General.Plugins.OnSectorCeilingSurfaceUpdate(this, ref ceilvertices); + surfaceentry.ceilvertices = ceilvertices; + surfaceentry.ceiltexture = longceiltexname; + if (surfaceentry.floorvertices == null) + surfaceentry.floorvertices = ceilvertices; + + // Update entry + surfaceentry = General.Map.CRenderer2D.Surfaces.UpdateSurfaces(surfaceentry); + General.Map.CRenderer2D.Surfaces.UnlockBuffers(); + } + + // This updates the sector when changes have been made + public void UpdateCache() + { + // Update if needed + if (updateneeded) + { + Triangulate(); + + CreateSurfaces(); + + General.Map.CRenderer2D.Surfaces.UnlockBuffers(); + } + } + + // Selected + protected override void DoSelect() + { + base.DoSelect(); + selecteditem = map.SelectedSectors.AddLast(this); + } + + // Deselect + protected override void DoUnselect() + { + base.DoUnselect(); + if (selecteditem.List != null) selecteditem.List.Remove(selecteditem); + selecteditem = null; + } + + #endregion + + #region ================== Methods + + // This checks if the given point is inside the sector polygon + public bool Intersect(Vector2D p) + { + uint c = 0; + + // Go for all sidedefs + foreach (Sidedef sd in sidedefs) + { + // Get vertices + Vector2D v1 = sd.Line.Start.Position; + Vector2D v2 = sd.Line.End.Position; + + // Determine min/max values + float miny = Math.Min(v1.y, v2.y); + float maxy = Math.Max(v1.y, v2.y); + float maxx = Math.Max(v1.x, v2.x); + + // Check for intersection + if ((p.y > miny) && (p.y <= maxy)) + { + if (p.x <= maxx) + { + if (v1.y != v2.y) + { + float xint = (p.y - v1.y) * (v2.x - v1.x) / (v2.y - v1.y) + v1.x; + if ((v1.x == v2.x) || (p.x <= xint)) c++; + } + } + } + } + + // Inside this polygon? + return ((c & 0x00000001UL) != 0); + } + + // This creates a bounding box rectangle + // This requires the sector triangulation to be up-to-date! + private RectangleF CreateBBox() + { + // Setup + float left = float.MaxValue; + float top = float.MaxValue; + float right = float.MinValue; + float bottom = float.MinValue; + + // Go for vertices + foreach (Vector2D v in triangles.Vertices) + { + // Update rect + if (v.x < left) left = v.x; + if (v.y < top) top = v.y; + if (v.x > right) right = v.x; + if (v.y > bottom) bottom = v.y; + } + + // Return rectangle + return new RectangleF(left, top, right - left, bottom - top); + } + + // This joins the sector with another sector + // This sector will be disposed + public void Join(Sector other) + { + // Any sidedefs to move? + if (sidedefs.Count > 0) + { + // Change secter reference on my sidedefs + // This automatically disposes this sector + while (sidedefs != null) + sidedefs.First.Value.SetSector(other); + } + else + { + // No sidedefs attached + // Dispose manually + this.Dispose(); + } + + General.Map.IsChanged = true; + } + + // String representation + public override string ToString() + { + return "Sector " + listindex; + } + + #endregion + + #region ================== Changes + + // This updates all properties + public void Update(int hfloor, int hceil, string tfloor, string tceil, int effect, int tag, int brightness) + { + BeforePropsChange(); + + // Apply changes + this.floorheight = hfloor; + this.ceilheight = hceil; + SetFloorTexture(tfloor); + SetCeilTexture(tceil); + this.effect = effect; + this.tag = tag; + this.brightness = brightness; + updateneeded = true; + } + + // villsa - new overload method + private Lights GetLight(int cindex, Lights[] light) + { + Lights color; + + if (cindex >= 256) + { + cindex -= 256; + color = light[cindex]; + } + else + { + byte c = (byte)cindex; + + color = new Lights(c, c, c, 0); + } + + color.color.a = 255; + + return color; + } + + private Lights GetLight(int color) + { + PixelColor c; + + c = PixelColor.FromInt(color); + return new Lights(c.r, c.g, c.b, 0); + } + + // villsa TODO - too many fucking overloads for this. Need to simplify the way lighting is handled... + public void Update(Dictionary flags, int hfloor, int hceil, + string tfloor, string tceil, int effect, int tag, Lights[] light, int[] cindex) + { + BeforePropsChange(); + + // Apply changes + this.flags = new Dictionary(flags); + this.floorheight = hfloor; + this.ceilheight = hceil; + SetFloorTexture(tfloor); + SetCeilTexture(tceil); + this.effect = effect; + this.tag = tag; + this.ceilColor = GetLight(cindex[1], light); + this.flrColor = GetLight(cindex[0], light); + this.thingColor = GetLight(cindex[2], light); + this.topColor = GetLight(cindex[3], light); + this.lwrColor = GetLight(cindex[4], light); + this.brightness = 255; + updateneeded = true; + } + + public void Update(Dictionary flags, int hfloor, int hceil, + string tfloor, string tceil, int effect, int tag, int[] colors) + { + BeforePropsChange(); + + // Apply changes + this.flags = new Dictionary(flags); + this.floorheight = hfloor; + this.ceilheight = hceil; + SetFloorTexture(tfloor); + SetCeilTexture(tceil); + this.effect = effect; + this.tag = tag; + this.brightness = 255; + this.flrColor = GetLight(colors[0]); + this.ceilColor = GetLight(colors[1]); + this.thingColor = GetLight(colors[2]); + this.topColor = GetLight(colors[3]); + this.lwrColor = GetLight(colors[4]); + updateneeded = true; + } + + // [villsa start] + // This checks and returns a flag without creating it + public bool IsFlagSet(string flagname) + { + if (flags.ContainsKey(flagname)) + return flags[flagname]; + else + return false; + } + + // This sets a flag + public void SetFlag(string flagname, bool value) + { + if (!flags.ContainsKey(flagname) || (IsFlagSet(flagname) != value)) + { + BeforePropsChange(); + flags[flagname] = value; + } + } + + // This returns a copy of the flags dictionary + public Dictionary GetFlags() + { + return new Dictionary(flags); + } + + // This clears all flags + public void ClearFlags() + { + flags.Clear(); + } + + // [villsa end] + + // This sets texture + public void SetFloorTexture(string name) + { + BeforePropsChange(); + + floortexname = name; + longfloortexname = Lump.MakeLongName(name); + updateneeded = true; + General.Map.IsChanged = true; + } + + // This sets texture + public void SetCeilTexture(string name) + { + BeforePropsChange(); + + ceiltexname = name; + longceiltexname = Lump.MakeLongName(name); + updateneeded = true; + General.Map.IsChanged = true; + } + + #endregion + } +} diff --git a/Source/Core/Map/SelectableElement.cs b/Source/Core/Map/SelectableElement.cs new file mode 100644 index 0000000..6484bb3 --- /dev/null +++ b/Source/Core/Map/SelectableElement.cs @@ -0,0 +1,116 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using CodeImp.DoomBuilder.Geometry; +using CodeImp.DoomBuilder.Rendering; +using SlimDX.Direct3D9; +using System.Drawing; + +#endregion + +namespace CodeImp.DoomBuilder.Map +{ + public abstract class SelectableElement : MapElement + { + #region ================== Constants + + #endregion + + #region ================== Variables + + // Selected or not? + private bool selected; + + // Group bitmask + private int groups; + + #endregion + + #region ================== Properties + + public bool Selected { get { return selected; } set { if (value && !selected) DoSelect(); else if (!value && selected) DoUnselect(); } } + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + internal SelectableElement() + { + } + + // Disposer + public override void Dispose() + { + // Remove from selection + if (selected) Selected = false; + + // Done + base.Dispose(); + } + + #endregion + + #region ================== Methods + + // This makes the selection + protected virtual void DoSelect() + { + selected = true; + } + + // This removes the selection + protected virtual void DoUnselect() + { + selected = false; + } + + // This copies properties to any other element + public void CopyPropertiesTo(SelectableElement element) + { + element.groups = this.groups; + element.Selected = this.selected; + base.CopyPropertiesTo(element); + } + + // This adds the element to one or more groups + public void AddToGroup(int groupsmask) + { + groups |= groupsmask; + } + + // This removes the elements from one or more groups + public void RemoveFromGroup(int groupsmask) + { + groups &= ~groupsmask; + } + + // This selects by group + public void SelectByGroup(int groupsmask) + { + this.Selected = ((groups & groupsmask) != 0); + } + + #endregion + } +} diff --git a/Source/Core/Map/SelectionType.cs b/Source/Core/Map/SelectionType.cs new file mode 100644 index 0000000..f080fdb --- /dev/null +++ b/Source/Core/Map/SelectionType.cs @@ -0,0 +1,43 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using CodeImp.DoomBuilder.Geometry; +using CodeImp.DoomBuilder.Rendering; +using SlimDX.Direct3D9; +using System.Drawing; + +#endregion + +namespace CodeImp.DoomBuilder.Map +{ + [Flags] + public enum SelectionType + { + None = 0, + Vertices = 1, + Linedefs = 2, + Sectors = 4, + Things = 8, + All = 0x7FFFFFFF, + } +} diff --git a/Source/Core/Map/Sidedef.cs b/Source/Core/Map/Sidedef.cs new file mode 100644 index 0000000..f230fec --- /dev/null +++ b/Source/Core/Map/Sidedef.cs @@ -0,0 +1,520 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Geometry; +using CodeImp.DoomBuilder.Types; + +#endregion + +namespace CodeImp.DoomBuilder.Map +{ + public sealed class Sidedef : MapElement + { + #region ================== Constants + + #endregion + + #region ================== Variables + + // Map + private MapSet map; + + // List items + private LinkedListNode sectorlistitem; + + // Owner + private Linedef linedef; + + // Sector + private Sector sector; + + // Properties + private int offsetx; + private int offsety; + private string texnamehigh; + private string texnamemid; + private string texnamelow; + private long longtexnamehigh; + private long longtexnamemid; + private long longtexnamelow; + + // villsa + private uint hashtexhigh; + private uint hashtexmid; + private uint hashtexlow; + + // Clone + private int serializedindex; + + #endregion + + #region ================== Properties + + public MapSet Map { get { return map; } } + public bool IsFront { get { return (linedef != null) ? (this == linedef.Front) : false; } } + public Linedef Line { get { return linedef; } } + public Sidedef Other { get { if (this == linedef.Front) return linedef.Back; else return linedef.Front; } } + public Sector Sector { get { return sector; } } + public float Angle { get { if (IsFront) return linedef.Angle; else return Angle2D.Normalized(linedef.Angle + Angle2D.PI); } } + public int OffsetX { get { return offsetx; } set { BeforePropsChange(); offsetx = value; } } + public int OffsetY { get { return offsety; } set { BeforePropsChange(); offsety = value; } } + public string HighTexture { get { return texnamehigh; } } + public string MiddleTexture { get { return texnamemid; } } + public string LowTexture { get { return texnamelow; } } + public long LongHighTexture { get { return longtexnamehigh; } } + public long LongMiddleTexture { get { return longtexnamemid; } } + public long LongLowTexture { get { return longtexnamelow; } } + internal int SerializedIndex { get { return serializedindex; } set { serializedindex = value; } } + + // villsa + public uint HashTexHigh { get { return hashtexhigh; } set { hashtexhigh = value; } } + public uint HashTexMid { get { return hashtexmid; } set { hashtexmid = value; } } + public uint HashTexLow { get { return hashtexlow; } set { hashtexlow = value; } } + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + internal Sidedef(MapSet map, int listindex, Linedef l, bool front, Sector s) + { + // Initialize + this.map = map; + this.listindex = listindex; + this.texnamehigh = "-"; + this.texnamemid = "-"; + this.texnamelow = "-"; + this.longtexnamehigh = MapSet.EmptyLongName; + this.longtexnamemid = MapSet.EmptyLongName; + this.longtexnamelow = MapSet.EmptyLongName; + + // Attach linedef + this.linedef = l; + if (l != null) + { + if (front) + l.AttachFrontP(this); + else + l.AttachBackP(this); + } + + // Attach sector + SetSectorP(s); + + if (map == General.Map.Map) + General.Map.UndoRedo.RecAddSidedef(this); + + // We have no destructor + GC.SuppressFinalize(this); + } + + // Disposer + public override void Dispose() + { + // Not already disposed? + if (!isdisposed) + { + // Already set isdisposed so that changes can be prohibited + isdisposed = true; + + if (map == General.Map.Map) + General.Map.UndoRedo.RecRemSidedef(this); + + // Remove from main list + map.RemoveSidedef(listindex); + + // Detach from linedef + if (linedef != null) linedef.DetachSidedefP(this); + + // Detach from sector + SetSectorP(null); + + // Clean up + sectorlistitem = null; + linedef = null; + map = null; + sector = null; + + // Dispose base + base.Dispose(); + } + } + + #endregion + + #region ================== Management + + // Call this before changing properties + protected override void BeforePropsChange() + { + if (map == General.Map.Map) + General.Map.UndoRedo.RecPrpSidedef(this); + } + + // Serialize / deserialize (passive: this doesn't record) + internal void ReadWrite(IReadWriteStream s) + { + if (!s.IsWriting) BeforePropsChange(); + + base.ReadWrite(s); + + s.rwInt(ref offsetx); + s.rwInt(ref offsety); + s.rwString(ref texnamehigh); + s.rwString(ref texnamemid); + s.rwString(ref texnamelow); + s.rwLong(ref longtexnamehigh); + s.rwLong(ref longtexnamemid); + s.rwLong(ref longtexnamelow); + } + + // This copies all properties to another sidedef + public void CopyPropertiesTo(Sidedef s) + { + s.BeforePropsChange(); + + // Copy properties + s.offsetx = offsetx; + s.offsety = offsety; + s.texnamehigh = texnamehigh; + s.texnamemid = texnamemid; + s.texnamelow = texnamelow; + s.longtexnamehigh = longtexnamehigh; + s.longtexnamemid = longtexnamemid; + s.longtexnamelow = longtexnamelow; + base.CopyPropertiesTo(s); + } + + // This changes sector + public void SetSector(Sector newsector) + { + if (map == General.Map.Map) + General.Map.UndoRedo.RecRefSidedefSector(this); + + // Change sector + SetSectorP(newsector); + } + + internal void SetSectorP(Sector newsector) + { + // Detach from sector + if (sector != null) sector.DetachSidedefP(sectorlistitem); + + // Change sector + sector = newsector; + + // Attach to sector + if (sector != null) + sectorlistitem = sector.AttachSidedefP(this); + + General.Map.IsChanged = true; + } + + // This sets the linedef + public void SetLinedef(Linedef ld, bool front) + { + if (linedef != null) linedef.DetachSidedefP(this); + + if (ld != null) + { + if (front) + ld.AttachFront(this); + else + ld.AttachBack(this); + } + } + + // This sets the linedef (passive: this doesn't tell the linedef and doesn't record) + internal void SetLinedefP(Linedef ld) + { + linedef = ld; + } + + #endregion + + #region ================== Methods + + // This removes textures that are not required + public void RemoveUnneededTextures(bool removemiddle) + { + RemoveUnneededTextures(removemiddle, false); + } + + // This removes textures that are not required + public void RemoveUnneededTextures(bool removemiddle, bool force) + { + BeforePropsChange(); + + // The middle texture can be removed regardless of any sector tag or linedef action + if (!MiddleRequired() && removemiddle) + { + this.texnamemid = "-"; + this.longtexnamemid = MapSet.EmptyLongName; + General.Map.IsChanged = true; + } + + // Check if the line or sectors have no action or tags because + // if they do, any texture on this side could be needed + if (((linedef.Tag <= 0) && (linedef.Action == 0) && (sector.Tag <= 0) && + ((Other == null) || (Other.sector.Tag <= 0))) || + force) + { + if (!HighRequired()) + { + this.texnamehigh = "-"; + this.longtexnamehigh = MapSet.EmptyLongName; + General.Map.IsChanged = true; + } + + if (!LowRequired()) + { + this.texnamelow = "-"; + this.longtexnamelow = MapSet.EmptyLongName; + General.Map.IsChanged = true; + } + } + } + + /// + /// This checks if a texture is required + /// + public bool HighRequired() + { + // Doublesided? + if (Other != null) + { + // Texture is required when ceiling of other side is lower + return (Other.sector.CeilHeight < this.sector.CeilHeight); + } + else + { + return false; + } + } + + /// + /// This checks if a texture is required + /// + public bool MiddleRequired() + { + // Texture is required when the line is singlesided + return (Other == null); + } + + /// + /// This checks if a texture is required + /// + public bool LowRequired() + { + // Doublesided? + if (Other != null) + { + // Texture is required when floor of other side is higher + return (Other.sector.FloorHeight > this.sector.FloorHeight); + } + else + { + return false; + } + } + + /// + /// This returns the height of the upper wall part. Returns 0 when no upper part exists. + /// + public int GetHighHeight() + { + Sidedef other = this.Other; + if (other != null) + { + int top = this.sector.CeilHeight; + int bottom = other.sector.CeilHeight; + int height = top - bottom; + return (height > 0) ? height : 0; + } + else + { + return 0; + } + } + + /// + /// This returns the height of the middle wall part. + /// + public int GetMiddleHeight() + { + Sidedef other = this.Other; + if (other != null) + { + int top = Math.Min(this.Sector.CeilHeight, other.Sector.CeilHeight); + int bottom = Math.Max(this.Sector.FloorHeight, other.Sector.FloorHeight); + int height = top - bottom; + return (height > 0) ? height : 0; + } + else + { + int top = this.Sector.CeilHeight; + int bottom = this.Sector.FloorHeight; + int height = top - bottom; + return (height > 0) ? height : 0; + } + } + + /// + /// This returns the height of the lower wall part. Returns 0 when no lower part exists. + /// + public int GetLowHeight() + { + Sidedef other = this.Other; + if (other != null) + { + int top = other.sector.FloorHeight; + int bottom = this.sector.FloorHeight; + int height = top - bottom; + return (height > 0) ? height : 0; + } + else + { + return 0; + } + } + + // This creates a checksum from the sidedef properties + // Used for faster sidedefs compression + public uint GetChecksum() + { + CRC crc = new CRC(); + crc.Add(sector.FixedIndex); + crc.Add(offsetx); + crc.Add(offsety); + crc.Add(longtexnamehigh); + crc.Add(longtexnamelow); + crc.Add(longtexnamemid); + return (uint)(crc.Value & 0x00000000FFFFFFFF); + } + + // This copies textures to another sidedef + // And possibly also the offsets + public void AddTexturesTo(Sidedef s) + { + int copyoffsets = 0; + + // s cannot be null + if (s == null) return; + + s.BeforePropsChange(); + + // Upper texture set? + if ((texnamehigh.Length > 0) && (texnamehigh[0] != '-')) + { + // Copy upper texture + s.texnamehigh = texnamehigh; + s.longtexnamehigh = longtexnamehigh; + + // Counts as a half coice for copying offsets + copyoffsets += 1; + } + + // Middle texture set? + if ((texnamemid.Length > 0) && (texnamemid[0] != '-')) + { + // Copy middle texture + s.texnamemid = texnamemid; + s.longtexnamemid = longtexnamemid; + + // Counts for copying offsets + copyoffsets += 2; + } + + // Lower texture set? + if ((texnamelow.Length > 0) && (texnamelow[0] != '-')) + { + // Copy middle texture + s.texnamelow = texnamelow; + s.longtexnamelow = longtexnamelow; + + // Counts as a half coice for copying offsets + copyoffsets += 1; + } + + // Copy offsets also? + if (copyoffsets >= 2) + { + // Copy offsets + s.offsetx = offsetx; + s.offsety = offsety; + } + + General.Map.IsChanged = true; + } + + #endregion + + #region ================== Changes + + // This updates all properties + public void Update(int offsetx, int offsety, string thigh, string tmid, string tlow) + { + BeforePropsChange(); + + // Apply changes + this.offsetx = offsetx; + this.offsety = offsety; + SetTextureHigh(thigh); + SetTextureMid(tmid); + SetTextureLow(tlow); + } + + // This sets texture + public void SetTextureHigh(string name) + { + BeforePropsChange(); + + texnamehigh = name; + longtexnamehigh = Lump.MakeLongName(name); + General.Map.IsChanged = true; + } + + // This sets texture + public void SetTextureMid(string name) + { + BeforePropsChange(); + + texnamemid = name; + longtexnamemid = Lump.MakeLongName(name); + General.Map.IsChanged = true; + } + + // This sets texture + public void SetTextureLow(string name) + { + BeforePropsChange(); + + texnamelow = name; + longtexnamelow = Lump.MakeLongName(name); + General.Map.IsChanged = true; + } + + #endregion + } +} diff --git a/Source/Core/Map/SidedefPart.cs b/Source/Core/Map/SidedefPart.cs new file mode 100644 index 0000000..4d87021 --- /dev/null +++ b/Source/Core/Map/SidedefPart.cs @@ -0,0 +1,36 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; + +#endregion + +namespace CodeImp.DoomBuilder.Map +{ + public enum SidedefPart + { + None = 0, + Upper = 1, + Middle = 2, + Lower = 3 + } +} diff --git a/Source/Core/Map/Thing.cs b/Source/Core/Map/Thing.cs new file mode 100644 index 0000000..bbfde15 --- /dev/null +++ b/Source/Core/Map/Thing.cs @@ -0,0 +1,497 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using CodeImp.DoomBuilder.Geometry; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Config; +using System.Drawing; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.VisualModes; + +#endregion + +namespace CodeImp.DoomBuilder.Map +{ + public sealed class Thing : SelectableElement + { + #region ================== Constants + + public const int NUM_ARGS = 5; + + #endregion + + #region ================== Variables + + // Map + private MapSet map; + + // Sector + private Sector sector = null; + + // List items + private LinkedListNode selecteditem; + + // Properties + private int type; + private Vector3D pos; + private float angle; + private Dictionary flags; + private int tag; + private int action; + private int[] args; + + // Configuration + private float size; + private PixelColor color; + private bool fixedsize; + private float iconoffset; // Arrow or dot coordinate offset on the texture + + #endregion + + #region ================== Properties + + public MapSet Map { get { return map; } } + public int Type { get { return type; } set { BeforePropsChange(); type = value; } } + public Vector3D Position { get { return pos; } } + public float Angle { get { return angle; } } + public int AngleDeg { get { return (int)Angle2D.RadToDeg(angle); } } + internal Dictionary Flags { get { return flags; } } + public int Action { get { return action; } set { BeforePropsChange(); action = value; } } + public int[] Args { get { return args; } } + public float Size { get { return size; } } + public float IconOffset { get { return iconoffset; } } + public PixelColor Color { get { return color; } } + public bool FixedSize { get { return fixedsize; } } + public int Tag { get { return tag; } set { BeforePropsChange(); tag = value; if ((tag < General.Map.FormatInterface.MinTag) || (tag > General.Map.FormatInterface.MaxTag)) throw new ArgumentOutOfRangeException("Tag", "Invalid tag number"); } } + public Sector Sector { get { return sector; } } + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + internal Thing(MapSet map, int listindex) + { + // Initialize + this.map = map; + this.listindex = listindex; + this.flags = new Dictionary(); + this.args = new int[NUM_ARGS]; + + if (map == General.Map.Map) + General.Map.UndoRedo.RecAddThing(this); + + // We have no destructor + GC.SuppressFinalize(this); + } + + // Disposer + public override void Dispose() + { + // Not already disposed? + if (!isdisposed) + { + // Already set isdisposed so that changes can be prohibited + isdisposed = true; + + if (map == General.Map.Map) + General.Map.UndoRedo.RecRemThing(this); + + // Remove from main list + map.RemoveThing(listindex); + + // Remove from sector + //if(sector != null) sector.DetachThing(sectorlistitem); + + // Clean up + map = null; + sector = null; + + // Dispose base + base.Dispose(); + } + } + + #endregion + + #region ================== Management + + // Call this before changing properties + protected override void BeforePropsChange() + { + if (map == General.Map.Map) + General.Map.UndoRedo.RecPrpThing(this); + } + + // Serialize / deserialize + internal void ReadWrite(IReadWriteStream s) + { + if (!s.IsWriting) BeforePropsChange(); + + base.ReadWrite(s); + + if (s.IsWriting) + { + s.wInt(flags.Count); + + foreach (KeyValuePair f in flags) + { + s.wString(f.Key); + s.wBool(f.Value); + } + } + else + { + int c; s.rInt(out c); + + flags = new Dictionary(c); + for (int i = 0; i < c; i++) + { + string t; s.rString(out t); + bool b; s.rBool(out b); + flags.Add(t, b); + } + } + + s.rwInt(ref type); + s.rwVector3D(ref pos); + s.rwFloat(ref angle); + s.rwInt(ref tag); + s.rwInt(ref action); + for (int i = 0; i < NUM_ARGS; i++) s.rwInt(ref args[i]); + } + + // This copies all properties to another thing + public void CopyPropertiesTo(Thing t) + { + t.BeforePropsChange(); + + // Copy properties + t.type = type; + t.angle = angle; + t.pos = pos; + t.flags = new Dictionary(flags); + t.tag = tag; + t.action = action; + t.args = (int[])args.Clone(); + t.size = size; + t.color = color; + t.iconoffset = iconoffset; + t.fixedsize = fixedsize; + base.CopyPropertiesTo(t); + } + + // This determines which sector the thing is in and links it + public void DetermineSector() + { + Linedef nl; + + // Find the nearest linedef on the map + nl = map.NearestLinedef(pos); + if (nl != null) + { + // Check what side of line we are at + if (nl.SideOfLine(pos) < 0f) + { + // Front side + if (nl.Front != null) sector = nl.Front.Sector; else sector = null; + } + else + { + // Back side + if (nl.Back != null) sector = nl.Back.Sector; else sector = null; + } + } + else + { + sector = null; + } + } + + // This determines which sector the thing is in and links it + public void DetermineSector(VisualBlockMap blockmap) + { + Linedef nl; + + // Find nearest sectors using the blockmap + List possiblesectors = blockmap.GetBlock(blockmap.GetBlockCoordinates(pos)).Sectors; + + // Check in which sector we are + sector = null; + foreach (Sector s in possiblesectors) + { + if (s.Intersect(pos)) + { + sector = s; + break; + } + } + } + + // This translates the flags into UDMF fields + internal void TranslateToUDMF() + { + // First make a single integer with all flags + int bits = 0; + int flagbit = 0; + foreach (KeyValuePair f in flags) + if (int.TryParse(f.Key, out flagbit) && f.Value) bits |= flagbit; + + // Now make the new flags + flags.Clear(); + foreach (FlagTranslation f in General.Map.Config.ThingFlagsTranslation) + { + // Flag found in bits? + if ((bits & f.Flag) == f.Flag) + { + // Add fields and remove bits + bits &= ~f.Flag; + for (int i = 0; i < f.Fields.Count; i++) + flags[f.Fields[i]] = f.FieldValues[i]; + } + else + { + // Add fields with inverted value + for (int i = 0; i < f.Fields.Count; i++) + flags[f.Fields[i]] = !f.FieldValues[i]; + } + } + } + + // This translates UDMF fields back into the normal flags + internal void TranslateFromUDMF() + { + // Make copy of the flags + Dictionary oldfields = new Dictionary(flags); + + // Make the flags + flags.Clear(); + foreach (KeyValuePair f in General.Map.Config.ThingFlags) + { + // Flag must be numeric + int flagbit = 0; + if (int.TryParse(f.Key, out flagbit)) + { + foreach (FlagTranslation ft in General.Map.Config.ThingFlagsTranslation) + { + if (ft.Flag == flagbit) + { + // Only set this flag when the fields match + bool fieldsmatch = true; + for (int i = 0; i < ft.Fields.Count; i++) + { + if (!oldfields.ContainsKey(ft.Fields[i]) || (oldfields[ft.Fields[i]] != ft.FieldValues[i])) + { + fieldsmatch = false; + break; + } + } + + // Field match? Then add the flag. + if (fieldsmatch) + { + flags.Add(f.Key, true); + break; + } + } + } + } + } + } + + // Selected + protected override void DoSelect() + { + base.DoSelect(); + selecteditem = map.SelectedThings.AddLast(this); + } + + // Deselect + protected override void DoUnselect() + { + base.DoUnselect(); + if (selecteditem.List != null) selecteditem.List.Remove(selecteditem); + selecteditem = null; + } + + #endregion + + #region ================== Changes + + // This moves the thing + // NOTE: This does not update sector! (call DetermineSector) + public void Move(Vector3D newpos) + { + BeforePropsChange(); + + // Change position + this.pos = newpos; + General.Map.IsChanged = true; + } + + // This moves the thing + // NOTE: This does not update sector! (call DetermineSector) + public void Move(Vector2D newpos) + { + BeforePropsChange(); + + // Change position + this.pos = new Vector3D(newpos.x, newpos.y, pos.z); + General.Map.IsChanged = true; + } + + // This moves the thing + // NOTE: This does not update sector! (call DetermineSector) + public void Move(float x, float y, float zoffset) + { + BeforePropsChange(); + + // Change position + this.pos = new Vector3D(x, y, zoffset); + General.Map.IsChanged = true; + } + + // This rotates the thing + public void Rotate(float newangle) + { + BeforePropsChange(); + + // Change angle + this.angle = newangle; + General.Map.IsChanged = true; + } + + // This updates all properties + // NOTE: This does not update sector! (call DetermineSector) + public void Update(int type, float x, float y, float zoffset, float angle, + Dictionary flags, int tag, int action, int[] args) + { + // Apply changes + this.type = type; + this.angle = angle; + this.flags = new Dictionary(flags); + this.tag = tag; + this.action = action; + this.args = new int[NUM_ARGS]; + args.CopyTo(this.args, 0); + this.Move(x, y, zoffset); + } + + // This updates the settings from configuration + public void UpdateConfiguration() + { + ThingTypeInfo ti; + + // Lookup settings + ti = General.Map.Data.GetThingInfo(type); + + // Apply size + size = ti.Radius; + fixedsize = ti.FixedSize; + + // Color valid? + if ((ti.Color >= 0) && (ti.Color < ColorCollection.NUM_THING_COLORS)) + { + // Apply color + color = General.Colors.Colors[ti.Color + ColorCollection.THING_COLORS_OFFSET]; + } + else + { + // Unknown thing color + color = General.Colors.Colors[ColorCollection.THING_COLORS_OFFSET]; + } + + // Apply icon offset (arrow or dot) + if (ti.Arrow) iconoffset = 0f; else iconoffset = 0.25f; + } + + #endregion + + #region ================== Methods + + // This checks and returns a flag without creating it + public bool IsFlagSet(string flagname) + { + if (flags.ContainsKey(flagname)) + return flags[flagname]; + else + return false; + } + + // This sets a flag + public void SetFlag(string flagname, bool value) + { + if (!flags.ContainsKey(flagname) || (IsFlagSet(flagname) != value)) + { + BeforePropsChange(); + + flags[flagname] = value; + } + } + + // This returns a copy of the flags dictionary + public Dictionary GetFlags() + { + return new Dictionary(flags); + } + + // This clears all flags + public void ClearFlags() + { + BeforePropsChange(); + + flags.Clear(); + } + + // This snaps the vertex to the grid + public void SnapToGrid() + { + // Calculate nearest grid coordinates + this.Move(General.Map.Grid.SnappedToGrid((Vector2D)pos)); + } + + // This snaps the vertex to the map format accuracy + public void SnapToAccuracy() + { + // Round the coordinates + Vector3D newpos = new Vector3D((float)Math.Round(pos.x, General.Map.FormatInterface.VertexDecimals), + (float)Math.Round(pos.y, General.Map.FormatInterface.VertexDecimals), + (float)Math.Round(pos.z, General.Map.FormatInterface.VertexDecimals)); + this.Move(newpos); + } + + // This returns the distance from given coordinates + public float DistanceToSq(Vector2D p) + { + return Vector2D.DistanceSq(p, pos); + } + + // This returns the distance from given coordinates + public float DistanceTo(Vector2D p) + { + return Vector2D.Distance(p, pos); + } + + #endregion + } +} diff --git a/Source/Core/Map/UniFields.cs b/Source/Core/Map/UniFields.cs new file mode 100644 index 0000000..14c2620 --- /dev/null +++ b/Source/Core/Map/UniFields.cs @@ -0,0 +1,70 @@ +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; + +#endregion + +namespace CodeImp.DoomBuilder.Map +{ + /// + /// List of universal fields and their values. + /// + public class UniFields : SortedList + { + // Owner of this list + protected MapElement owner; + public MapElement Owner { get { return owner; } internal set { owner = value; } } + + // New constructor + /// + public UniFields() : base(2) + { + } + + // New constructor + /// + public UniFields(int capacity) : base(capacity) + { + } + + // Copy constructor (makes a deep copy) + /// + public UniFields(UniFields copyfrom) : base(copyfrom.Count) + { + foreach (KeyValuePair v in copyfrom) + this.Add(v.Key, new UniValue(v.Value)); + } + + // New constructor + /// + public UniFields(MapElement owner) : base(2) + { + this.owner = owner; + } + + // New constructor + /// + public UniFields(MapElement owner, int capacity) : base(capacity) + { + this.owner = owner; + } + + // Copy constructor + /// + public UniFields(MapElement owner, UniFields copyfrom) : base(copyfrom) + { + this.owner = owner; + } + + /// Call this before making changes to the fields, or they may not be updated correctly with undo/redo! + public void BeforeFieldsChange() + { + if (owner != null) + owner.BeforeFieldsChange(); + } + } +} diff --git a/Source/Core/Map/UniValue.cs b/Source/Core/Map/UniValue.cs new file mode 100644 index 0000000..fd5e862 --- /dev/null +++ b/Source/Core/Map/UniValue.cs @@ -0,0 +1,184 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using CodeImp.DoomBuilder.Geometry; +using CodeImp.DoomBuilder.Rendering; +using SlimDX.Direct3D9; +using System.Drawing; +using CodeImp.DoomBuilder.Types; +using CodeImp.DoomBuilder.IO; + +#endregion + +namespace CodeImp.DoomBuilder.Map +{ + public class UniValue + { + #region ================== Constants + + private const string NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789_"; + private const string START_CHARS = "abcdefghijklmnopqrstuvwxyz_"; + + #endregion + + #region ================== Variables + + private object value; + private int type; + + #endregion + + #region ================== Properties + + public object Value + { + get + { + return this.value; + } + + set + { + // Value may only be a primitive type + if ((!(value is int) && !(value is float) && !(value is string) && !(value is bool)) || (value == null)) + throw new ArgumentException("Universal field values can only be of type int, float, string or bool."); + + this.value = value; + } + } + + public int Type { get { return this.type; } set { this.type = value; } } + + #endregion + + #region ================== Constructor + + // Constructor + public UniValue(int type, object value) + { + this.type = type; + this.value = value; + + // We have no destructor + GC.SuppressFinalize(this); + } + + // Constructor + public UniValue(UniValue v) + { + this.type = v.type; + this.value = v.value; + + // We have no destructor + GC.SuppressFinalize(this); + } + + // Constructor + public UniValue() + { + // We have no destructor + GC.SuppressFinalize(this); + } + + #endregion + + #region ================== Methods + + // Serialize / deserialize + internal void ReadWrite(IReadWriteStream s) + { + s.rwInt(ref type); + switch ((UniversalType)type) + { + case UniversalType.AngleRadians: + case UniversalType.AngleDegreesFloat: + case UniversalType.Float: + { + float v = 0.0f; + try { v = (float)value; } catch (NullReferenceException e) { } + s.rwFloat(ref v); + value = v; + break; + } + + case UniversalType.AngleDegrees: + case UniversalType.Color: + case UniversalType.EnumBits: + case UniversalType.EnumOption: + case UniversalType.Integer: + case UniversalType.LinedefTag: + case UniversalType.LinedefType: + case UniversalType.SectorEffect: + case UniversalType.SectorTag: + case UniversalType.ThingTag: + { + int v = 0; + try { v = (int)value; } catch (NullReferenceException e) { } + s.rwInt(ref v); + value = v; + break; + } + + case UniversalType.Boolean: + { + bool v = false; + try { v = (bool)value; } catch (NullReferenceException e) { } + s.rwBool(ref v); + value = v; + break; + } + + case UniversalType.Flat: + case UniversalType.String: + case UniversalType.Texture: + case UniversalType.EnumStrings: + { + string v = (string)value; + s.rwString(ref v); + value = v; + break; + } + + default: + General.Fail("Unknown field type to read/write!"); + break; + } + } + + // This validates a UDMF field name and returns the valid part + public static string ValidateName(string name) + { + // Keep only valid characters + string fieldname = name.Trim().ToLowerInvariant(); + string validname = ""; + for (int c = 0; c < fieldname.Length; c++) + { + string valid_chars = (validname.Length > 0) ? NAME_CHARS : START_CHARS; + if (valid_chars.IndexOf(fieldname[c]) > -1) validname += fieldname[c]; + } + return validname; + } + + #endregion + } +} diff --git a/Source/Core/Map/Vertex.cs b/Source/Core/Map/Vertex.cs new file mode 100644 index 0000000..6ebb2b0 --- /dev/null +++ b/Source/Core/Map/Vertex.cs @@ -0,0 +1,303 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using CodeImp.DoomBuilder.Geometry; +using CodeImp.DoomBuilder.Rendering; +using SlimDX.Direct3D9; +using System.Drawing; +using CodeImp.DoomBuilder.IO; + +#endregion + +namespace CodeImp.DoomBuilder.Map +{ + public sealed class Vertex : SelectableElement + { + #region ================== Constants + + public const int BUFFERVERTICES = 1; + public const int RENDERPRIMITIVES = 1; + + #endregion + + #region ================== Variables + + // Map + private MapSet map; + + // List items + private LinkedListNode selecteditem; + + // Position + private Vector2D pos; + + // References + private LinkedList linedefs; + + // Cloning + private Vertex clone; + private int serializedindex; + + #endregion + + #region ================== Properties + + public MapSet Map { get { return map; } } + public ICollection Linedefs { get { return linedefs; } } + public Vector2D Position { get { return pos; } } + internal Vertex Clone { get { return clone; } set { clone = value; } } + internal int SerializedIndex { get { return serializedindex; } set { serializedindex = value; } } + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + internal Vertex(MapSet map, int listindex, Vector2D pos) + { + // Initialize + this.map = map; + this.linedefs = new LinkedList(); + this.listindex = listindex; + this.pos = pos; + + if (map == General.Map.Map) + General.Map.UndoRedo.RecAddVertex(this); + + // We have no destructor + GC.SuppressFinalize(this); + } + + // Disposer + public override void Dispose() + { + // Not already disposed? + if (!isdisposed) + { + // Already set isdisposed so that changes can be prohibited + isdisposed = true; + + if (map.AutoRemove) + { + // Dispose the lines that are attached to this vertex + // because a linedef cannot exist without 2 vertices. + foreach (Linedef ld in linedefs) ld.Dispose(); + } + else + { + // Detach from linedefs + foreach (Linedef ld in linedefs) ld.DetachVertexP(this); + } + + if (map == General.Map.Map) + General.Map.UndoRedo.RecRemVertex(this); + + // Remove from main list + map.RemoveVertex(listindex); + + // Clean up + linedefs = null; + map = null; + + // Dispose base + base.Dispose(); + } + } + + #endregion + + #region ================== Management + + // Call this before changing properties + protected override void BeforePropsChange() + { + if (map == General.Map.Map) + General.Map.UndoRedo.RecPrpVertex(this); + } + + // This attaches a linedef and returns the listitem + internal LinkedListNode AttachLinedefP(Linedef l) + { + return linedefs.AddLast(l); + } + + // This detaches a linedef + internal void DetachLinedefP(LinkedListNode l) + { + // Not disposing? + if (!isdisposed) + { + // Remove linedef + linedefs.Remove(l); + + // No more linedefs left? + if ((linedefs.Count == 0) && map.AutoRemove) + { + // This vertex is now useless, dispose it + this.Dispose(); + } + } + } + + // Serialize / deserialize + internal void ReadWrite(IReadWriteStream s) + { + if (!s.IsWriting) BeforePropsChange(); + + base.ReadWrite(s); + + s.rwVector2D(ref pos); + + if (s.IsWriting) + { + // Let all lines know they need an update + foreach (Linedef l in linedefs) l.NeedUpdate(); + } + } + + // Selected + protected override void DoSelect() + { + base.DoSelect(); + selecteditem = map.SelectedVertices.AddLast(this); + } + + // Deselect + protected override void DoUnselect() + { + base.DoUnselect(); + if (selecteditem.List != null) selecteditem.List.Remove(selecteditem); + selecteditem = null; + } + + #endregion + + #region ================== Methods + + // This copies all properties to another thing + public void CopyPropertiesTo(Vertex v) + { + v.BeforePropsChange(); + + // Copy properties + v.pos = pos; + base.CopyPropertiesTo(v); + } + + // This returns the distance from given coordinates + public float DistanceToSq(Vector2D p) + { + return (p.x - pos.x) * (p.x - pos.x) + (p.y - pos.y) * (p.y - pos.y); + } + + // This returns the distance from given coordinates + public float DistanceTo(Vector2D p) + { + return Vector2D.Distance(p, pos); + } + + // This finds the line closest to the specified position + public Linedef NearestLinedef(Vector2D pos) { return MapSet.NearestLinedef(linedefs, pos); } + + // This moves the vertex + public void Move(Vector2D newpos) + { + // Do we actually move? + if (newpos != pos) + { + BeforePropsChange(); + + // Change position + pos = newpos; + +#if DEBUG + if(float.IsNaN(pos.x) || float.IsNaN(pos.y) || + float.IsInfinity(pos.x) || float.IsInfinity(pos.y)) + { + General.Fail("Invalid vertex position! The given vertex coordinates cannot be NaN or Infinite."); + } +#endif + + // Let all lines know they need an update + foreach (Linedef l in linedefs) l.NeedUpdate(); + General.Map.IsChanged = true; + } + } + + // This snaps the vertex to the map format accuracy + public void SnapToAccuracy() + { + // Round the coordinates + Vector2D newpos = new Vector2D((float)Math.Round(pos.x, General.Map.FormatInterface.VertexDecimals), + (float)Math.Round(pos.y, General.Map.FormatInterface.VertexDecimals)); + this.Move(newpos); + } + + // This snaps the vertex to the grid + public void SnapToGrid() + { + // Calculate nearest grid coordinates + this.Move(General.Map.Grid.SnappedToGrid(pos)); + } + + // This joins another vertex + // Which means this vertex is removed and the other is kept! + public void Join(Vertex other) + { + // If either of the two vertices was selected, keep the other selected + if (this.Selected) other.Selected = true; + if (this.marked) other.marked = true; + + // Any linedefs to move? + if (linedefs.Count > 0) + { + // Detach all linedefs and attach them to the other + // This will automatically dispose this vertex + while (linedefs != null) + { + // Move the line to the other vertex + if (linedefs.First.Value.Start == this) + linedefs.First.Value.SetStartVertex(other); + else + linedefs.First.Value.SetEndVertex(other); + } + } + else + { + // No lines attached + // Dispose manually + this.Dispose(); + } + + General.Map.IsChanged = true; + } + + // String representation + public override string ToString() + { + return "Vertex (" + pos + ")"; + } + + #endregion + } +} diff --git a/Source/Core/Plugins/Plug.cs b/Source/Core/Plugins/Plug.cs new file mode 100644 index 0000000..200bda5 --- /dev/null +++ b/Source/Core/Plugins/Plug.cs @@ -0,0 +1,343 @@ +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.IO; +using CodeImp.DoomBuilder.Editing; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Data; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Windows; +using CodeImp.DoomBuilder.Config; + +#endregion + +namespace CodeImp.DoomBuilder.Plugins +{ + /// + /// This is the key link between the Doom Builder core and the plugin. + /// Every plugin must expose a single class that inherits this class. + /// + public class Plug : IDisposable + { + #region ================== Constants + + #endregion + + #region ================== Variables + + // Internals + private Plugin plugin; + + // Disposing + private bool isdisposed = false; + + #endregion + + #region ================== Properties + + // Internals + internal Plugin Plugin { get { return plugin; } set { plugin = value; } } + + /// + /// Indicates if the plugin has been disposed. + /// + public bool IsDisposed { get { return isdisposed; } } + + /// + /// Override this to return a more descriptive name for your plugin. + /// Default is the library filename without extension. + /// + public virtual string Name { get { return plugin.Name; } } + + /// + /// Override this to return the minimum revision of the Doom Builder 2 core that is + /// required to use this plugin. You can find the revision number in the About dialog, + /// it is the right most part of the version number. + /// + public virtual int MinimumRevision { get { return 0; } } + + #endregion + + #region ================== Constructor / Disposer + + /// + /// This is the key link between the Doom Builder core and the plugin. + /// Every plugin must expose a single class that inherits this class. + /// + /// NOTE: Some methods cannot be used in this constructor, because the plugin + /// is not yet fully initialized. Instead, use the Initialize method to do + /// your initializations. + /// + /// + public Plug() + { + // Initialize + + // We have no destructor + GC.SuppressFinalize(this); + } + + /// + /// This is called by the Doom Builder core when the plugin is being disposed. + /// + public virtual void Dispose() + { + // Not already disposed? + if (!isdisposed) + { + // Clean up + plugin = null; + + // Done + isdisposed = true; + } + } + + #endregion + + #region ================== Methods + + /// + /// This finds the embedded resource with the specified name in the plugin and creates + /// a Stream from it. Returns null when the embedded resource cannot be found. + /// + /// Name of the resource in the plugin. + /// Returns a Stream of the embedded resource, + /// or null when the resource cannot be found. + public Stream GetResourceStream(string resourcename) + { + return plugin.GetResourceStream(resourcename); + } + + #endregion + + #region ================== Events + + /// + /// This iscalled when the ceiling surface buffer is updated for a sector. The plugin can + /// modify the vertices to change how the surface is presented to the user. + /// + public virtual void OnSectorCeilingSurfaceUpdate(Sector s, ref FlatVertex[] vertices) + { + } + + /// + /// This iscalled when the floor surface buffer is updated for a sector. The plugin can + /// modify the vertices to change how the surface is presented to the user. + /// + public virtual void OnSectorFloorSurfaceUpdate(Sector s, ref FlatVertex[] vertices) + { + } + + /// + /// Occurs before a map is opened. + /// + public virtual void OnMapOpenBegin() + { + } + + /// + /// Occurs after a map is opened. + /// + public virtual void OnMapOpenEnd() + { + } + + /// + /// Occurs before a new map is created. + /// + public virtual void OnMapNewBegin() + { + } + + /// + /// Occurs after a new map is created. + /// + public virtual void OnMapNewEnd() + { + } + + /// + /// Occurs before the map is closed. + /// + public virtual void OnMapCloseBegin() + { + } + + /// + /// Occurs after a the map is closed. + /// + public virtual void OnMapCloseEnd() + { + } + + /// + /// Occurs before a map is saved. + /// + public virtual void OnMapSaveBegin(SavePurpose purpose) + { + } + + /// + /// Occurs after a map is saved. + /// + public virtual void OnMapSaveEnd(SavePurpose purpose) + { + } + + /// + /// Occurs before the MapSet is changed. This means that the active MapSet will be disposed and changed to a new one. + /// + public virtual void OnMapSetChangeBegin() + { + } + + /// + /// Occurs after the MapSet is changed. + /// + public virtual void OnMapSetChangeEnd() + { + } + + /// + /// This is called after the constructor to allow a plugin to initialize. + /// + public virtual void OnInitialize() + { + } + + /// + /// This is called when the user chose to reload the resources. + /// + public virtual void OnReloadResources() + { + } + + /// + /// This is called by the Doom Builder core when the editing mode changes. + /// Return false to abort the mode change. + /// + /// The previous editing mode + /// The new editing mode + public virtual bool OnModeChange(EditMode oldmode, EditMode newmode) + { + return true; + } + + /// + /// Called by the Doom Builder core when the user changes the program configuration (F5). + /// + public virtual void OnProgramReconfigure() + { + } + + /// + /// Called by the Doom Builder core when the user changes the map settings (F2). + /// + public virtual void OnMapReconfigure() + { + } + + /// + /// Called by the Doom Builder core when the user wants to copy selected geometry. + /// Return false to abort the copy operation. + /// The result parameter is false when the operation was already aborted by another plugin. + /// + public virtual bool OnCopyBegin(bool result) + { + return true; + } + + /// + /// Called by the Doom Builder core when the user has copied geometry. + /// + public virtual void OnCopyEnd() + { + } + + /// + /// Called by the Doom Builder core when the user wants to paste geometry into the map. + /// Return false to abort the paste operation. + /// The result parameter is false when the operation was already aborted by another plugin. + /// + public virtual bool OnPasteBegin(PasteOptions options, bool result) + { + return true; + } + + /// + /// Called by the Doom Builder core when the user pastes geometry into the map. The new geometry is created and marked before this method is called. + /// + public virtual void OnPasteEnd(PasteOptions options) + { + } + + /// + /// Called by the Doom Builder core when the user wants to undo the previous action. + /// Return false to abort the operation. + /// The result parameter is false when the operation was already aborted by another plugin. + /// + public virtual bool OnUndoBegin(bool result) + { + return true; + } + + /// + /// Called by the Doom Builder core when the user has undone the previous action. + /// + public virtual void OnUndoEnd() + { + } + + /// + /// Called by the Doom Builder core when the user wants to redo the previously undone action. + /// Return false to abort the operation. + /// The result parameter is false when the operation was already aborted by another plugin. + /// + public virtual bool OnRedoBegin(bool result) + { + return true; + } + + /// + /// Called by the Doom Builder core when the user has redone the action. + /// + public virtual void OnRedoEnd() + { + } + + /// + /// Called by the Doom Builder core when a new undo level has been created. + /// + public virtual void OnUndoCreated() + { + } + + /// + /// Called by the Doom Builder core when an undo level has been withdrawn. + /// + public virtual void OnUndoWithdrawn() + { + } + + /// + /// Called when the user opens the Preferences dialog. + /// + public virtual void OnShowPreferences(PreferencesController controller) + { + } + + /// + /// Called when the user closes the Preferences dialog by either accepting the changes or cancelling. + /// + public virtual void OnClosePreferences(PreferencesController controller) + { + } + + #endregion + } +} diff --git a/Source/Core/Plugins/Plugin.cs b/Source/Core/Plugins/Plugin.cs new file mode 100644 index 0000000..615732a --- /dev/null +++ b/Source/Core/Plugins/Plugin.cs @@ -0,0 +1,215 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.IO; +using System.Reflection; +using CodeImp.DoomBuilder.Actions; +using CodeImp.DoomBuilder.Editing; + +#endregion + +namespace CodeImp.DoomBuilder.Plugins +{ + internal class Plugin : IDisposable + { + #region ================== Constants + + #endregion + + #region ================== Variables + + // The plugin assembly + private Assembly asm; + + // The plug + private Plug plug; + + // Unique name used to refer to this assembly + private string name; + + // Disposing + private bool isdisposed = false; + + #endregion + + #region ================== Properties + + public Assembly Assembly { get { return asm; } } + public Plug Plug { get { return plug; } } + public string Name { get { return name; } } + public bool IsDisposed { get { return isdisposed; } } + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + public Plugin(string filename) + { + // Initialize + string shortfilename = Path.GetFileName(filename); + name = Path.GetFileNameWithoutExtension(filename); + General.WriteLogLine("Loading plugin '" + name + "' from '" + shortfilename + "'..."); + + try + { + // Load assembly + asm = Assembly.LoadFile(filename); + } + catch (Exception) + { + General.ErrorLogger.Add(ErrorType.Error, "Could not load plugin \"" + shortfilename + "\", the DLL file could not be read. This file is not supposed to be in the Plugins subdirectory."); + throw new InvalidProgramException(); + } + + // Find the class that inherits from Plugin + Type t = FindSingleClass(typeof(Plug)); + if (t != null) + { + // Are the multiple plug classes? + if (FindClasses(typeof(Plug)).Length > 1) + { + // Show a warning + General.ErrorLogger.Add(ErrorType.Warning, "Plugin \"" + shortfilename + "\" has more than one plug. The following class is used to create in instance: " + t.FullName); + } + + // Make plug instance + plug = CreateObject(t); + plug.Plugin = this; + + // Verify minimum revision number + int thisrevision = General.ThisAssembly.GetName().Version.Revision; + if ((thisrevision != 0) && (plug.MinimumRevision > thisrevision)) + { + // Can't load this plugin because it is meant for a newer version + General.ErrorLogger.Add(ErrorType.Error, "Could not load plugin \"" + shortfilename + "\", the Plugin is made for Doom Builder 2 core revision " + plug.MinimumRevision + " and you are running revision " + thisrevision + "."); + throw new InvalidProgramException(); + } + } + else + { + // How can we plug something in without a plug? + General.ErrorLogger.Add(ErrorType.Error, "Could not load plugin \"" + shortfilename + "\", plugin is missing the plug. This file is not supposed to be in the Plugins subdirectory."); + throw new InvalidProgramException(); + } + + // We have no destructor + GC.SuppressFinalize(this); + } + + // Disposer + public void Dispose() + { + // Not already disposed? + if (!isdisposed) + { + // Clean up + asm = null; + + // Done + isdisposed = true; + } + } + + #endregion + + #region ================== Methods + + // This creates a stream to read a resource or returns null when not found + public Stream GetResourceStream(string resourcename) + { + string[] resnames; + + // Find a resource + resnames = asm.GetManifestResourceNames(); + foreach (string rn in resnames) + { + // Found it? + if (rn.EndsWith(resourcename, StringComparison.InvariantCultureIgnoreCase)) + { + // Get a stream from the resource + return asm.GetManifestResourceStream(rn); + } + } + + // Nothing found + return null; + } + + // This finds all class types that inherits from the given type + public Type[] FindClasses(Type t) + { + List found = new List(); + Type[] types; + + // Get all exported types + types = asm.GetExportedTypes(); + foreach (Type it in types) + { + // Compare types + if (t.IsAssignableFrom(it)) found.Add(it); + } + + // Return list + return found.ToArray(); + } + + // This finds a single class type that inherits from the given type + // Returns null when no valid type was found + public Type FindSingleClass(Type t) + { + Type[] types = FindClasses(t); + if (types.Length > 0) return types[0]; else return null; + } + + // This creates an instance of a class + public T CreateObject(Type t, params object[] args) + { + return CreateObjectA(t, args); + } + + // This creates an instance of a class + public T CreateObjectA(Type t, object[] args) + { + try + { + // Create instance + return (T)asm.CreateInstance(t.FullName, false, BindingFlags.Default, null, args, CultureInfo.CurrentCulture, new object[0]); + } + catch (TargetInvocationException e) + { + // Error! + General.ErrorLogger.Add(ErrorType.Error, "Failed to create class instance '" + t.Name + "' from plugin '" + name + "' " + e.InnerException.GetType().Name + " at target: " + e.InnerException.Message); + return default(T); + } + catch (Exception e) + { + // Error! + General.ErrorLogger.Add(ErrorType.Error, "Failed to create class instance '" + t.Name + "' from plugin '" + name + "' " + e.GetType().Name + ": " + e.Message); + return default(T); + } + } + + #endregion + } +} diff --git a/Source/Core/Plugins/PluginManager.cs b/Source/Core/Plugins/PluginManager.cs new file mode 100644 index 0000000..1cbbc45 --- /dev/null +++ b/Source/Core/Plugins/PluginManager.cs @@ -0,0 +1,334 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.IO; +using CodeImp.DoomBuilder.Editing; +using System.Reflection; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Windows; +using CodeImp.DoomBuilder.Config; + +#endregion + +namespace CodeImp.DoomBuilder.Plugins +{ + internal class PluginManager + { + #region ================== Constants + + #endregion + + #region ================== Variables + + // Plugins + private List plugins; + + // Disposing + private bool isdisposed = false; + + #endregion + + #region ================== Properties + + internal List Plugins { get { return plugins; } } + public bool IsDisposed { get { return isdisposed; } } + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + public PluginManager() + { + // Make lists + this.plugins = new List(); + + // We have no destructor + GC.SuppressFinalize(this); + } + + // Disposer + public void Dispose() + { + // Not already disposed? + if (!isdisposed) + { + // Clean up + foreach (Plugin p in plugins) p.Dispose(); + + // Done + isdisposed = true; + } + } + + #endregion + + #region ================== Methods + + // This creates a list of assemblies + public List GetPluginAssemblies() + { + List asms = new List(plugins.Count); + foreach (Plugin p in plugins) + asms.Add(p.Assembly); + return asms; + } + + + // This loads all plugins + public void LoadAllPlugins() + { + string[] filenames; + Type[] editclasses; + EditModeAttribute[] emattrs; + EditModeInfo editmodeinfo; + Plugin p; + + // Find all .dll files + filenames = Directory.GetFiles(General.PluginsPath, "*.dll", SearchOption.TopDirectoryOnly); + foreach (string fn in filenames) + { + // Load plugin from this file + try + { + p = new Plugin(fn); + } + catch (InvalidProgramException) + { + p = null; + } + + // Continue if no errors + if ((p != null) && (!p.IsDisposed)) + { + // Add to plugins + this.plugins.Add(p); + + // Load actions + General.Actions.LoadActions(p.Assembly); + + // Plugin is now initialized + p.Plug.OnInitialize(); + } + } + } + + // This returns a plugin by assembly, or null when plugin cannot be found + public Plugin FindPluginByAssembly(Assembly assembly) + { + // Go for all plugins the find the one with matching assembly + foreach (Plugin p in plugins) + { + if (p.Assembly == assembly) return p; + } + + // Nothing found + return null; + } + + #endregion + + #region ================== Events + + + public void ReloadResources() + { + foreach (Plugin p in plugins) p.Plug.OnReloadResources(); + } + + + public bool ModeChanges(EditMode oldmode, EditMode newmode) + { + bool result = true; + foreach (Plugin p in plugins) result &= p.Plug.OnModeChange(oldmode, newmode); + return result; + } + + + public void ProgramReconfigure() + { + foreach (Plugin p in plugins) p.Plug.OnProgramReconfigure(); + } + + + public void MapReconfigure() + { + foreach (Plugin p in plugins) p.Plug.OnMapReconfigure(); + } + + + public bool OnCopyBegin() + { + bool result = true; + foreach (Plugin p in plugins) result &= p.Plug.OnCopyBegin(result); + return result; + } + + + public void OnCopyEnd() + { + foreach (Plugin p in plugins) p.Plug.OnCopyEnd(); + } + + + public bool OnPasteBegin(PasteOptions options) + { + bool result = true; + foreach (Plugin p in plugins) result &= p.Plug.OnPasteBegin(options.Copy(), result); + return result; + } + + + public void OnPasteEnd(PasteOptions options) + { + foreach (Plugin p in plugins) p.Plug.OnPasteEnd(options.Copy()); + } + + + public bool OnUndoBegin() + { + bool result = true; + foreach (Plugin p in plugins) result &= p.Plug.OnUndoBegin(result); + return result; + } + + + public void OnUndoEnd() + { + foreach (Plugin p in plugins) p.Plug.OnUndoEnd(); + } + + + public bool OnRedoBegin() + { + bool result = true; + foreach (Plugin p in plugins) result &= p.Plug.OnRedoBegin(result); + return result; + } + + + public void OnRedoEnd() + { + foreach (Plugin p in plugins) p.Plug.OnRedoEnd(); + } + + + public void OnUndoCreated() + { + foreach (Plugin p in plugins) p.Plug.OnUndoCreated(); + } + + + public void OnUndoWithdrawn() + { + foreach (Plugin p in plugins) p.Plug.OnUndoWithdrawn(); + } + + + public void OnMapOpenBegin() + { + foreach (Plugin p in plugins) p.Plug.OnMapOpenBegin(); + } + + + public void OnMapOpenEnd() + { + foreach (Plugin p in plugins) p.Plug.OnMapOpenEnd(); + } + + + public void OnMapNewBegin() + { + foreach (Plugin p in plugins) p.Plug.OnMapNewBegin(); + } + + + public void OnMapNewEnd() + { + foreach (Plugin p in plugins) p.Plug.OnMapNewEnd(); + } + + + public void OnMapCloseBegin() + { + foreach (Plugin p in plugins) p.Plug.OnMapCloseBegin(); + } + + + public void OnMapCloseEnd() + { + foreach (Plugin p in plugins) p.Plug.OnMapCloseEnd(); + } + + + public void OnMapSaveBegin(SavePurpose purpose) + { + foreach (Plugin p in plugins) p.Plug.OnMapSaveBegin(purpose); + } + + + public void OnMapSaveEnd(SavePurpose purpose) + { + foreach (Plugin p in plugins) p.Plug.OnMapSaveEnd(purpose); + } + + + public void OnMapSetChangeBegin() + { + foreach (Plugin p in plugins) p.Plug.OnMapSetChangeBegin(); + } + + + public void OnMapSetChangeEnd() + { + foreach (Plugin p in plugins) p.Plug.OnMapSetChangeEnd(); + } + + + public void OnSectorCeilingSurfaceUpdate(Sector s, ref FlatVertex[] vertices) + { + foreach (Plugin p in plugins) p.Plug.OnSectorCeilingSurfaceUpdate(s, ref vertices); + } + + + public void OnSectorFloorSurfaceUpdate(Sector s, ref FlatVertex[] vertices) + { + foreach (Plugin p in plugins) p.Plug.OnSectorFloorSurfaceUpdate(s, ref vertices); + } + + + public void OnShowPreferences(PreferencesController controller) + { + foreach (Plugin p in plugins) p.Plug.OnShowPreferences(controller); + } + + + public void OnClosePreferences(PreferencesController controller) + { + foreach (Plugin p in plugins) p.Plug.OnClosePreferences(controller); + } + + #endregion + } +} diff --git a/Source/Core/Properties/AssemblyInfo.cs b/Source/Core/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..174ea0c --- /dev/null +++ b/Source/Core/Properties/AssemblyInfo.cs @@ -0,0 +1,32 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Doom Builder")] +[assembly: AssemblyDescription("Doom, Heretic and Hexen map editor")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("CodeImp")] +[assembly: AssemblyProduct("Doom Builder")] +[assembly: AssemblyCopyright("Copyright © 2007")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("767cd97a-8b1f-42b3-9086-a5ab9cdbe4ab")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +[assembly: AssemblyVersion("2.0.1.0")] diff --git a/Source/Core/Properties/Resources.Designer.cs b/Source/Core/Properties/Resources.Designer.cs new file mode 100644 index 0000000..8d59fca --- /dev/null +++ b/Source/Core/Properties/Resources.Designer.cs @@ -0,0 +1,733 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace CodeImp.DoomBuilder.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("CodeImp.DoomBuilder.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap CLogo { + get { + object obj = ResourceManager.GetObject("CLogo", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap Close { + get { + object obj = ResourceManager.GetObject("Close", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap ColorPick { + get { + object obj = ResourceManager.GetObject("ColorPick", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap Copy { + get { + object obj = ResourceManager.GetObject("Copy", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap Cut { + get { + object obj = ResourceManager.GetObject("Cut", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap ErrorLarge { + get { + object obj = ResourceManager.GetObject("ErrorLarge", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap Failed { + get { + object obj = ResourceManager.GetObject("Failed", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap File { + get { + object obj = ResourceManager.GetObject("File", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap Filter { + get { + object obj = ResourceManager.GetObject("Filter", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap Folder { + get { + object obj = ResourceManager.GetObject("Folder", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap FullBrightness { + get { + object obj = ResourceManager.GetObject("FullBrightness", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap Grid2 { + get { + object obj = ResourceManager.GetObject("Grid2", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap Grid2_arrowup { + get { + object obj = ResourceManager.GetObject("Grid2_arrowup", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap Grid4 { + get { + object obj = ResourceManager.GetObject("Grid4", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap Help { + get { + object obj = ResourceManager.GetObject("Help", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap Hourglass { + get { + object obj = ResourceManager.GetObject("Hourglass", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap KnownTextureSet { + get { + object obj = ResourceManager.GetObject("KnownTextureSet", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap mergegeometry { + get { + object obj = ResourceManager.GetObject("mergegeometry", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap mergegeometry2 { + get { + object obj = ResourceManager.GetObject("mergegeometry2", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap MissingTexture { + get { + object obj = ResourceManager.GetObject("MissingTexture", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap Monster2 { + get { + object obj = ResourceManager.GetObject("Monster2", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap Monster3 { + get { + object obj = ResourceManager.GetObject("Monster3", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap NewMap { + get { + object obj = ResourceManager.GetObject("NewMap", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap NewScript { + get { + object obj = ResourceManager.GetObject("NewScript", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap OpenMap { + get { + object obj = ResourceManager.GetObject("OpenMap", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap OpenScript { + get { + object obj = ResourceManager.GetObject("OpenScript", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap Paste { + get { + object obj = ResourceManager.GetObject("Paste", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap PasteSpecial { + get { + object obj = ResourceManager.GetObject("PasteSpecial", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap Prefab { + get { + object obj = ResourceManager.GetObject("Prefab", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap Prefab2 { + get { + object obj = ResourceManager.GetObject("Prefab2", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap Properties { + get { + object obj = ResourceManager.GetObject("Properties", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap Question { + get { + object obj = ResourceManager.GetObject("Question", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap Redo { + get { + object obj = ResourceManager.GetObject("Redo", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap SaveAll { + get { + object obj = ResourceManager.GetObject("SaveAll", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap SaveMap { + get { + object obj = ResourceManager.GetObject("SaveMap", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap SaveScript { + get { + object obj = ResourceManager.GetObject("SaveScript", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap Script2 { + get { + object obj = ResourceManager.GetObject("Script2", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap ScriptCompile { + get { + object obj = ResourceManager.GetObject("ScriptCompile", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Byte[]. + /// + internal static byte[] ScriptConstant { + get { + object obj = ResourceManager.GetObject("ScriptConstant", resourceCulture); + return ((byte[])(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Byte[]. + /// + internal static byte[] ScriptError { + get { + object obj = ResourceManager.GetObject("ScriptError", resourceCulture); + return ((byte[])(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap ScriptHelp { + get { + object obj = ResourceManager.GetObject("ScriptHelp", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Byte[]. + /// + internal static byte[] ScriptKeyword { + get { + object obj = ResourceManager.GetObject("ScriptKeyword", resourceCulture); + return ((byte[])(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap ScriptPalette { + get { + object obj = ResourceManager.GetObject("ScriptPalette", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap SlimDX_small { + get { + object obj = ResourceManager.GetObject("SlimDX_small", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap Splash3_small { + get { + object obj = ResourceManager.GetObject("Splash3_small", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap Splash3_trans { + get { + object obj = ResourceManager.GetObject("Splash3_trans", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap Status0 { + get { + object obj = ResourceManager.GetObject("Status0", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap Status1 { + get { + object obj = ResourceManager.GetObject("Status1", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap Status10 { + get { + object obj = ResourceManager.GetObject("Status10", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap Status11 { + get { + object obj = ResourceManager.GetObject("Status11", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap Status12 { + get { + object obj = ResourceManager.GetObject("Status12", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap Status2 { + get { + object obj = ResourceManager.GetObject("Status2", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap Test { + get { + object obj = ResourceManager.GetObject("Test", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap treeview { + get { + object obj = ResourceManager.GetObject("treeview", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap Undo { + get { + object obj = ResourceManager.GetObject("Undo", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap UnknownImage { + get { + object obj = ResourceManager.GetObject("UnknownImage", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap ViewColorCeiling { + get { + object obj = ResourceManager.GetObject("ViewColorCeiling", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap ViewColorFloor { + get { + object obj = ResourceManager.GetObject("ViewColorFloor", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap ViewColorThing { + get { + object obj = ResourceManager.GetObject("ViewColorThing", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap ViewNormal { + get { + object obj = ResourceManager.GetObject("ViewNormal", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap ViewTextureCeiling { + get { + object obj = ResourceManager.GetObject("ViewTextureCeiling", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap ViewTextureFloor { + get { + object obj = ResourceManager.GetObject("ViewTextureFloor", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap Warning { + get { + object obj = ResourceManager.GetObject("Warning", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap WarningLarge { + get { + object obj = ResourceManager.GetObject("WarningLarge", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap WarningOff { + get { + object obj = ResourceManager.GetObject("WarningOff", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap Zoom { + get { + object obj = ResourceManager.GetObject("Zoom", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap Zoom_arrowup { + get { + object obj = ResourceManager.GetObject("Zoom_arrowup", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + } +} diff --git a/Source/Core/Properties/Resources.resx b/Source/Core/Properties/Resources.resx new file mode 100644 index 0000000..64f86f2 --- /dev/null +++ b/Source/Core/Properties/Resources.resx @@ -0,0 +1,322 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + ..\Resources\Grid2.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\Grid2_arrowup.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\ScriptConstant.xpm;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + ..\Resources\UnknownImage.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\Hourglass.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\Status11.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\Status10.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\mergegeometry.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\CLogo.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\Status1.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\ScriptError.xpm;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + ..\Resources\Script2.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\Cut.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\Close.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\Status2.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\KnownTextureSet.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\Status12.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\Redo.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\OpenMap.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\ViewTextureFloor.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\Zoom_arrowup.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\treeview.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\Monster2.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\WarningLarge.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\NewMap.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\Copy.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\ScriptPalette.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\NewMap2.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\ViewNormal.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\SaveMap.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\Undo.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\Failed.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\Status0.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\ScriptHelp.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\Prefab.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\Splash3_trans.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\Zoom.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\FullBrightness.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\Test.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\Monster3.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\Properties.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\SlimDX_small.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\WarningOff.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\ViewTextureCeiling.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\Help.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\Splash3_small.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\mergegeometry2.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\Prefab2.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\Question.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\SaveAll.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\ColorPick.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\Filter.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\ScriptKeyword.xpm;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + ..\Resources\ScriptCompile.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\SaveScript.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\OpenScript.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\Paste.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\Folder.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\Grid4.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\MissingTexture.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\ErrorLarge.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\Warning.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\NewScript.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\PasteSpecial.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\ViewColorCeiling.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\ViewColorFloor.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\ViewColorThing.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + \ No newline at end of file diff --git a/Source/Core/Rendering/ColorCollection.cs b/Source/Core/Rendering/ColorCollection.cs new file mode 100644 index 0000000..50ef1bb --- /dev/null +++ b/Source/Core/Rendering/ColorCollection.cs @@ -0,0 +1,287 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Reflection; +using System.Drawing; +using CodeImp.DoomBuilder.IO; +using SlimDX.Direct3D9; +using SlimDX; + +using Configuration = CodeImp.DoomBuilder.IO.Configuration; + +#endregion + +namespace CodeImp.DoomBuilder.Rendering +{ + public sealed class ColorCollection + { + #region ================== Constants + + // Assist color creation + private const float BRIGHT_MULTIPLIER = 1.0f; + private const float BRIGHT_ADDITION = 0.4f; + private const float DARK_MULTIPLIER = 0.9f; + private const float DARK_ADDITION = -0.2f; + + // Palette size + private const int NUM_COLORS = 44; // villsa - changed to 44 + public const int NUM_THING_COLORS = 20; + public const int THING_COLORS_OFFSET = 20; + + // Colors! + public const int BACKGROUND = 0; + public const int VERTICES = 1; + public const int LINEDEFS = 2; + public const int ACTIONS = 3; + public const int SOUNDS = 4; + public const int HIGHLIGHT = 5; + public const int SELECTION = 6; + public const int INDICATION = 7; + public const int GRID = 8; + public const int GRID64 = 9; + public const int CROSSHAIR3D = 10; + public const int HIGHLIGHT3D = 11; + public const int SELECTION3D = 12; + public const int SCRIPTBACKGROUND = 13; + public const int LINENUMBERS = 14; + public const int PLAINTEXT = 15; + public const int COMMENTS = 16; + public const int KEYWORDS = 17; + public const int LITERALS = 18; + public const int CONSTANTS = 19; + public const int THINGCOLOR00 = 20; + public const int THINGCOLOR01 = 21; + public const int THINGCOLOR02 = 22; + public const int THINGCOLOR03 = 23; + public const int THINGCOLOR04 = 24; + public const int THINGCOLOR05 = 25; + public const int THINGCOLOR06 = 26; + public const int THINGCOLOR07 = 27; + public const int THINGCOLOR08 = 28; + public const int THINGCOLOR09 = 29; + public const int THINGCOLOR10 = 30; + public const int THINGCOLOR11 = 31; + public const int THINGCOLOR12 = 32; + public const int THINGCOLOR13 = 33; + public const int THINGCOLOR14 = 34; + public const int THINGCOLOR15 = 35; + public const int THINGCOLOR16 = 36; + public const int THINGCOLOR17 = 37; + public const int THINGCOLOR18 = 38; + public const int THINGCOLOR19 = 39; + public const int INVISIBLE = 40; //villsa + public const int MBLOCK = 41; //villsa + public const int SECRET = 42; //villsa + public const int TAGONLY = 43; //villsa + + #endregion + + #region ================== Variables + + // Colors + private PixelColor[] colors; + private PixelColor[] brightcolors; + private PixelColor[] darkcolors; + + // Color-correction table + private byte[] correctiontable; + + #endregion + + #region ================== Properties + + public PixelColor[] Colors { get { return colors; } } + public PixelColor[] BrightColors { get { return brightcolors; } } + public PixelColor[] DarkColors { get { return darkcolors; } } + + public PixelColor Background { get { return colors[BACKGROUND]; } internal set { colors[BACKGROUND] = value; } } + public PixelColor Vertices { get { return colors[VERTICES]; } internal set { colors[VERTICES] = value; } } + public PixelColor Linedefs { get { return colors[LINEDEFS]; } internal set { colors[LINEDEFS] = value; } } + public PixelColor Actions { get { return colors[ACTIONS]; } internal set { colors[ACTIONS] = value; } } + public PixelColor Sounds { get { return colors[SOUNDS]; } internal set { colors[SOUNDS] = value; } } + public PixelColor Highlight { get { return colors[HIGHLIGHT]; } internal set { colors[HIGHLIGHT] = value; } } + public PixelColor Selection { get { return colors[SELECTION]; } internal set { colors[SELECTION] = value; } } + public PixelColor Indication { get { return colors[INDICATION]; } internal set { colors[INDICATION] = value; } } + public PixelColor Grid { get { return colors[GRID]; } internal set { colors[GRID] = value; } } + public PixelColor Grid64 { get { return colors[GRID64]; } internal set { colors[GRID64] = value; } } + public PixelColor Invisible { get { return colors[INVISIBLE]; } internal set { colors[INVISIBLE] = value; } } // villsa + public PixelColor MBlock { get { return colors[MBLOCK]; } internal set { colors[MBLOCK] = value; } } // villsa + public PixelColor Secret { get { return colors[SECRET]; } internal set { colors[SECRET] = value; } } // villsa + public PixelColor Tagonly { get { return colors[TAGONLY]; } internal set { colors[TAGONLY] = value; } } // villsa + + public PixelColor Crosshair3D { get { return colors[CROSSHAIR3D]; } internal set { colors[CROSSHAIR3D] = value; } } + public PixelColor Highlight3D { get { return colors[HIGHLIGHT3D]; } internal set { colors[HIGHLIGHT3D] = value; } } + public PixelColor Selection3D { get { return colors[SELECTION3D]; } internal set { colors[SELECTION3D] = value; } } + + public PixelColor ScriptBackground { get { return colors[SCRIPTBACKGROUND]; } internal set { colors[SCRIPTBACKGROUND] = value; } } + public PixelColor LineNumbers { get { return colors[LINENUMBERS]; } internal set { colors[LINENUMBERS] = value; } } + public PixelColor PlainText { get { return colors[PLAINTEXT]; } internal set { colors[PLAINTEXT] = value; } } + public PixelColor Comments { get { return colors[COMMENTS]; } internal set { colors[COMMENTS] = value; } } + public PixelColor Keywords { get { return colors[KEYWORDS]; } internal set { colors[KEYWORDS] = value; } } + public PixelColor Literals { get { return colors[LITERALS]; } internal set { colors[LITERALS] = value; } } + public PixelColor Constants { get { return colors[CONSTANTS]; } internal set { colors[CONSTANTS] = value; } } + + #endregion + + #region ================== Constructor / Disposer + + // Constructor for settings from configuration + internal ColorCollection(Configuration cfg) + { + // Initialize + colors = new PixelColor[NUM_COLORS]; + brightcolors = new PixelColor[NUM_COLORS]; + darkcolors = new PixelColor[NUM_COLORS]; + + // Read all colors from config + for (int i = 0; i < NUM_COLORS; i++) + { + // Read color + colors[i] = PixelColor.FromInt(cfg.ReadSetting("colors.color" + i.ToString(CultureInfo.InvariantCulture), 0)); + } + + // Set new colors + if (colors[THINGCOLOR00].ToInt() == 0) colors[THINGCOLOR00] = PixelColor.FromColor(Color.DimGray); + if (colors[THINGCOLOR01].ToInt() == 0) colors[THINGCOLOR01] = PixelColor.FromColor(Color.RoyalBlue); + if (colors[THINGCOLOR02].ToInt() == 0) colors[THINGCOLOR02] = PixelColor.FromColor(Color.ForestGreen); + if (colors[THINGCOLOR03].ToInt() == 0) colors[THINGCOLOR03] = PixelColor.FromColor(Color.LightSeaGreen); + if (colors[THINGCOLOR04].ToInt() == 0) colors[THINGCOLOR04] = PixelColor.FromColor(Color.Firebrick); + if (colors[THINGCOLOR05].ToInt() == 0) colors[THINGCOLOR05] = PixelColor.FromColor(Color.DarkViolet); + if (colors[THINGCOLOR06].ToInt() == 0) colors[THINGCOLOR06] = PixelColor.FromColor(Color.DarkGoldenrod); + if (colors[THINGCOLOR07].ToInt() == 0) colors[THINGCOLOR07] = PixelColor.FromColor(Color.Silver); + if (colors[THINGCOLOR08].ToInt() == 0) colors[THINGCOLOR08] = PixelColor.FromColor(Color.Gray); + if (colors[THINGCOLOR09].ToInt() == 0) colors[THINGCOLOR09] = PixelColor.FromColor(Color.DeepSkyBlue); + if (colors[THINGCOLOR10].ToInt() == 0) colors[THINGCOLOR10] = PixelColor.FromColor(Color.LimeGreen); + if (colors[THINGCOLOR11].ToInt() == 0) colors[THINGCOLOR11] = PixelColor.FromColor(Color.PaleTurquoise); + if (colors[THINGCOLOR12].ToInt() == 0) colors[THINGCOLOR12] = PixelColor.FromColor(Color.Tomato); + if (colors[THINGCOLOR13].ToInt() == 0) colors[THINGCOLOR13] = PixelColor.FromColor(Color.Violet); + if (colors[THINGCOLOR14].ToInt() == 0) colors[THINGCOLOR14] = PixelColor.FromColor(Color.Yellow); + if (colors[THINGCOLOR15].ToInt() == 0) colors[THINGCOLOR15] = PixelColor.FromColor(Color.WhiteSmoke); + if (colors[THINGCOLOR16].ToInt() == 0) colors[THINGCOLOR16] = PixelColor.FromColor(Color.LightPink); + if (colors[THINGCOLOR17].ToInt() == 0) colors[THINGCOLOR17] = PixelColor.FromColor(Color.DarkOrange); + if (colors[THINGCOLOR18].ToInt() == 0) colors[THINGCOLOR18] = PixelColor.FromColor(Color.DarkKhaki); + if (colors[THINGCOLOR19].ToInt() == 0) colors[THINGCOLOR19] = PixelColor.FromColor(Color.Goldenrod); + + // Create assist colors + CreateAssistColors(); + + // Create color correction table + CreateCorrectionTable(); + + // We have no destructor + GC.SuppressFinalize(this); + } + + #endregion + + #region ================== Methods + + // This generates a color-correction table + internal void CreateCorrectionTable() + { + // Determine amounts + float gamma = (float)(General.Settings.ImageBrightness + 10) * 0.1f; + float bright = (float)General.Settings.ImageBrightness * 5f; + + // Make table + correctiontable = new byte[256]; + + // Fill table + for (int i = 0; i < 256; i++) + { + byte b; + float a = (float)i * gamma + bright; + if (a < 0f) b = 0; else if (a > 255f) b = 255; else b = (byte)a; + correctiontable[i] = b; + } + } + + // This applies color-correction over a block of pixel data + internal unsafe void ApplColorCorrection(PixelColor* pixels, int numpixels) + { + for (PixelColor* cp = pixels + numpixels - 1; cp >= pixels; cp--) + { + cp->r = correctiontable[cp->r]; + cp->g = correctiontable[cp->g]; + cp->b = correctiontable[cp->b]; + } + } + + // This clamps a value between 0 and 1 + private float Saturate(float v) + { + if (v < 0f) return 0f; else if (v > 1f) return 1f; else return v; + } + + // This creates assist colors + internal void CreateAssistColors() + { + // Go for all colors + for (int i = 0; i < NUM_COLORS; i++) + { + // Create assist colors + brightcolors[i] = CreateBrightVariant(colors[i]); + darkcolors[i] = CreateDarkVariant(colors[i]); + } + } + + // This creates a brighter color + public PixelColor CreateBrightVariant(PixelColor pc) + { + Color4 o = pc.ToColorValue(); + Color4 c = new Color4(1f, 0f, 0f, 0f); + + // Create brighter color + c.Red = Saturate(o.Red * BRIGHT_MULTIPLIER + BRIGHT_ADDITION); + c.Green = Saturate(o.Green * BRIGHT_MULTIPLIER + BRIGHT_ADDITION); + c.Blue = Saturate(o.Blue * BRIGHT_MULTIPLIER + BRIGHT_ADDITION); + return PixelColor.FromInt(c.ToArgb()); + } + + // This creates a darker color + public PixelColor CreateDarkVariant(PixelColor pc) + { + Color4 o = pc.ToColorValue(); + Color4 c = new Color4(1f, 0f, 0f, 0f); + + // Create darker color + c.Red = Saturate(o.Red * DARK_MULTIPLIER + DARK_ADDITION); + c.Green = Saturate(o.Green * DARK_MULTIPLIER + DARK_ADDITION); + c.Blue = Saturate(o.Blue * DARK_MULTIPLIER + DARK_ADDITION); + return PixelColor.FromInt(c.ToArgb()); + } + + // This saves colors to configuration + internal void SaveColors(Configuration cfg) + { + // Write all colors to config + for (int i = 0; i < NUM_COLORS; i++) + { + // Write color + cfg.WriteSetting("colors.color" + i.ToString(CultureInfo.InvariantCulture), colors[i].ToInt()); + } + } + + #endregion + } +} diff --git a/Source/Core/Rendering/ColorSetting.cs b/Source/Core/Rendering/ColorSetting.cs new file mode 100644 index 0000000..124d0cb --- /dev/null +++ b/Source/Core/Rendering/ColorSetting.cs @@ -0,0 +1,95 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Reflection; +using System.Drawing; + +#endregion + +namespace CodeImp.DoomBuilder.Rendering +{ + internal sealed class ColorSetting : IEquatable + { + #region ================== Constants + + #endregion + + #region ================== Variables + + private string name; + private PixelColor color; + + #endregion + + #region ================== Properties + + public Color Color { get { return Color.FromArgb(color.ToInt()); } set { color = PixelColor.FromColor(value); } } + public PixelColor PixelColor { get { return color; } set { color = value; } } + public string Name { get { return name; } } + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + public ColorSetting(string name, PixelColor color) + { + // Initialize + this.name = name; + this.color = color; + + // We have no destructor + GC.SuppressFinalize(this); + } + + #endregion + + #region ================== Methods + + // This returns a PixelColor with adjusted alpha + public PixelColor WithAlpha(byte a) + { + return new PixelColor(color, a); + } + + // Equal? + public bool Equals(ColorSetting other) + { + return this.name == other.name; + } + + // To PixelColor + public static implicit operator PixelColor(ColorSetting c) + { + return c.color; + } + + // To Color + public static implicit operator Color(ColorSetting c) + { + return Color.FromArgb(c.color.ToInt()); + } + + #endregion + } +} diff --git a/Source/Core/Rendering/D3DDevice.cs b/Source/Core/Rendering/D3DDevice.cs new file mode 100644 index 0000000..bcd5432 --- /dev/null +++ b/Source/Core/Rendering/D3DDevice.cs @@ -0,0 +1,590 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using System.Drawing; +using SlimDX.Direct3D9; +using System.ComponentModel; +using CodeImp.DoomBuilder.Geometry; +using SlimDX; +using CodeImp.DoomBuilder.Windows; +using CodeImp.DoomBuilder.Data; + +using Configuration = CodeImp.DoomBuilder.IO.Configuration; +using CodeImp.DoomBuilder.Controls; + +#endregion + +namespace CodeImp.DoomBuilder.Rendering +{ + internal class D3DDevice + { + #region ================== Constants + + // NVPerfHUD device name + public const string NVPERFHUD_ADAPTER = "NVPerfHUD"; + + #endregion + + #region ================== Variables + + // Settings + private int adapter; + private Filter postfilter; + private Filter mipgeneratefilter; + + // Main objects + private static Direct3D d3d; + private RenderTargetControl rendertarget; + private Capabilities devicecaps; + private Device device; + private Viewport viewport; + private Dictionary resources; + private ShaderManager shaders; + private Surface backbuffer; + private Surface depthbuffer; + private TextFont font; + private ResourceImage fonttexture; + + // Disposing + private bool isdisposed = false; + + #endregion + + #region ================== Properties + + internal Device Device { get { return device; } } + public bool IsDisposed { get { return isdisposed; } } + internal RenderTargetControl RenderTarget { get { return rendertarget; } } + internal Viewport Viewport { get { return viewport; } } + internal ShaderManager Shaders { get { return shaders; } } + internal Surface BackBuffer { get { return backbuffer; } } + internal Surface DepthBuffer { get { return depthbuffer; } } + internal TextFont Font { get { return font; } } + internal Texture FontTexture { get { return fonttexture.Texture; } } + internal Filter PostFilter { get { return postfilter; } } + internal Filter MipGenerateFilter { get { return mipgeneratefilter; } } + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + internal D3DDevice(RenderTargetControl rendertarget) + { + // Set render target + this.rendertarget = rendertarget; + + // Create resources list + resources = new Dictionary(); + + // We have no destructor + GC.SuppressFinalize(this); + } + + // Disposer + internal void Dispose() + { + // Not already disposed? + if (!isdisposed) + { + // Clean up + foreach (ID3DResource res in resources.Values) res.UnloadResource(); + if (shaders != null) shaders.Dispose(); + rendertarget = null; + if (backbuffer != null) backbuffer.Dispose(); + if (depthbuffer != null) depthbuffer.Dispose(); + if (device != null) device.Dispose(); + if (font != null) font.Dispose(); + if (fonttexture != null) fonttexture.Dispose(); + + // Done + isdisposed = true; + } + } + + #endregion + + #region ================== Renderstates + + // This completes initialization after the device has started or has been reset + public void SetupSettings() + { + // Setup renderstates + device.SetRenderState(RenderState.AlphaBlendEnable, false); + device.SetRenderState(RenderState.AlphaBlendEnable, false); + device.SetRenderState(RenderState.AlphaFunc, Compare.GreaterEqual); + device.SetRenderState(RenderState.AlphaRef, 0x0000007E); + device.SetRenderState(RenderState.AlphaTestEnable, false); + device.SetRenderState(RenderState.Ambient, Color.White.ToArgb()); + device.SetRenderState(RenderState.AmbientMaterialSource, ColorSource.Material); + device.SetRenderState(RenderState.AntialiasedLineEnable, false); + device.SetRenderState(RenderState.Clipping, true); + device.SetRenderState(RenderState.ColorVertex, false); + device.SetRenderState(RenderState.ColorWriteEnable, ColorWriteEnable.Red | ColorWriteEnable.Green | ColorWriteEnable.Blue | ColorWriteEnable.Alpha); + device.SetRenderState(RenderState.CullMode, Cull.None); + device.SetRenderState(RenderState.DestinationBlend, Blend.InverseSourceAlpha); + device.SetRenderState(RenderState.DiffuseMaterialSource, ColorSource.Color1); + device.SetRenderState(RenderState.DitherEnable, true); + device.SetRenderState(RenderState.FillMode, FillMode.Solid); + device.SetRenderState(RenderState.FogEnable, false); + device.SetRenderState(RenderState.FogTableMode, FogMode.Linear); + device.SetRenderState(RenderState.Lighting, false); + device.SetRenderState(RenderState.LocalViewer, false); + device.SetRenderState(RenderState.NormalizeNormals, false); + device.SetRenderState(RenderState.PointSpriteEnable, false); + device.SetRenderState(RenderState.RangeFogEnable, false); + device.SetRenderState(RenderState.SourceBlend, Blend.SourceAlpha); + device.SetRenderState(RenderState.SpecularEnable, false); + device.SetRenderState(RenderState.StencilEnable, false); + device.SetRenderState(RenderState.TextureFactor, -1); + device.SetRenderState(RenderState.ZEnable, false); + device.SetRenderState(RenderState.ZWriteEnable, false); + device.PixelShader = null; + device.VertexShader = null; + + // Matrices + device.SetTransform(TransformState.World, Matrix.Identity); + device.SetTransform(TransformState.View, Matrix.Identity); + device.SetTransform(TransformState.Projection, Matrix.Identity); + + // Sampler settings + if (General.Settings.ClassicBilinear) + { + device.SetSamplerState(0, SamplerState.MagFilter, TextureFilter.Linear); + device.SetSamplerState(0, SamplerState.MinFilter, TextureFilter.Linear); + device.SetSamplerState(0, SamplerState.MipFilter, TextureFilter.Linear); + device.SetSamplerState(0, SamplerState.MipMapLodBias, 0f); + } + else + { + device.SetSamplerState(0, SamplerState.MagFilter, TextureFilter.Point); + device.SetSamplerState(0, SamplerState.MinFilter, TextureFilter.Point); + device.SetSamplerState(0, SamplerState.MipFilter, TextureFilter.Point); + device.SetSamplerState(0, SamplerState.MipMapLodBias, 0f); + } + + // Texture addressing + device.SetSamplerState(0, SamplerState.AddressU, TextureAddress.Wrap); + device.SetSamplerState(0, SamplerState.AddressV, TextureAddress.Wrap); + device.SetSamplerState(0, SamplerState.AddressW, TextureAddress.Wrap); + + // First texture stage + device.SetTextureStageState(0, TextureStage.ColorOperation, TextureOperation.Modulate); + device.SetTextureStageState(0, TextureStage.ColorArg1, TextureArgument.Texture); + device.SetTextureStageState(0, TextureStage.ColorArg2, TextureArgument.Diffuse); + device.SetTextureStageState(0, TextureStage.ResultArg, TextureArgument.Current); + device.SetTextureStageState(0, TextureStage.TexCoordIndex, 0); + + // Second texture stage + device.SetTextureStageState(1, TextureStage.ColorOperation, TextureOperation.Modulate); + device.SetTextureStageState(1, TextureStage.ColorArg1, TextureArgument.Current); + device.SetTextureStageState(1, TextureStage.ColorArg2, TextureArgument.TFactor); + device.SetTextureStageState(1, TextureStage.ResultArg, TextureArgument.Current); + device.SetTextureStageState(1, TextureStage.TexCoordIndex, 0); + + // No more further stages + device.SetTextureStageState(2, TextureStage.ColorOperation, TextureOperation.Disable); + + // First alpha stage + device.SetTextureStageState(0, TextureStage.AlphaOperation, TextureOperation.Modulate); + device.SetTextureStageState(0, TextureStage.AlphaArg1, TextureArgument.Texture); + device.SetTextureStageState(0, TextureStage.AlphaArg2, TextureArgument.Diffuse); + + // Second alpha stage + device.SetTextureStageState(1, TextureStage.AlphaOperation, TextureOperation.Modulate); + device.SetTextureStageState(1, TextureStage.AlphaArg1, TextureArgument.Current); + device.SetTextureStageState(1, TextureStage.AlphaArg2, TextureArgument.TFactor); + + // No more further stages + device.SetTextureStageState(2, TextureStage.AlphaOperation, TextureOperation.Disable); + + // Setup material + Material material = new Material(); + material.Ambient = new Color4(Color.White); + material.Diffuse = new Color4(Color.White); + material.Specular = new Color4(Color.White); + device.Material = material; + + // Shader settings + shaders.World3D.SetConstants(General.Settings.VisualBilinear, true); + + // Texture filters + postfilter = Filter.Point; + mipgeneratefilter = Filter.Box; + + // Initialize presentations + Presentation.Initialize(); + } + + #endregion + + #region ================== Initialization + + // This starts up Direct3D + public static void Startup() + { + d3d = new Direct3D(); + } + + // This terminates Direct3D + public static void Terminate() + { + if (d3d != null) + { + d3d.Dispose(); + d3d = null; + } + } + + // This initializes the graphics + public bool Initialize() + { + PresentParameters displaypp; + DeviceType devtype; + + // Use default adapter + this.adapter = 0; // Manager.Adapters.Default.Adapter; + + try + { + // Make present parameters + displaypp = CreatePresentParameters(adapter); + + // Determine device type for compatability with NVPerfHUD + if (d3d.Adapters[adapter].Details.Description.EndsWith(NVPERFHUD_ADAPTER)) + devtype = DeviceType.Reference; + else + devtype = DeviceType.Hardware; + + // Get the device capabilities + devicecaps = d3d.GetDeviceCaps(adapter, devtype); + + // Check if this adapter supports TnL + if ((devicecaps.DeviceCaps & DeviceCaps.HWTransformAndLight) != 0) + { + // Initialize with hardware TnL + device = new Device(d3d, adapter, devtype, rendertarget.Handle, + CreateFlags.HardwareVertexProcessing, displaypp); + } + else + { + // Initialize with software TnL + device = new Device(d3d, adapter, devtype, rendertarget.Handle, + CreateFlags.SoftwareVertexProcessing, displaypp); + } + } + catch (Exception) + { + // Failed + MessageBox.Show(General.MainWindow, "Unable to initialize the Direct3D video device. Another application may have taken exclusive mode on this video device or the device does not support Direct3D at all.", Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error); + return false; + } + + // Add event to cancel resize event + //device.DeviceResizing += new CancelEventHandler(CancelResize); + + // Keep a reference to the original buffers + backbuffer = device.GetBackBuffer(0, 0); + depthbuffer = device.DepthStencilSurface; + + // Get the viewport + viewport = device.Viewport; + + // Create shader manager + shaders = new ShaderManager(this); + + // Font + postfilter = Filter.Box; + font = new TextFont(); + fonttexture = new ResourceImage("CodeImp.DoomBuilder.Resources.Font.png"); + fonttexture.LoadImage(); + fonttexture.MipMapLevels = 2; + fonttexture.CreateTexture(); + + // Initialize settings + SetupSettings(); + + // Done + return true; + } + + // This is to disable the automatic resize reset + private static void CancelResize(object sender, CancelEventArgs e) + { + // Cancel resize event + e.Cancel = true; + } + + // This creates present parameters + private PresentParameters CreatePresentParameters(int adapter) + { + PresentParameters displaypp = new PresentParameters(); + DisplayMode currentmode; + + // Get current display mode + currentmode = d3d.Adapters[adapter].CurrentDisplayMode; + + // Make present parameters + displaypp.Windowed = true; + displaypp.SwapEffect = SwapEffect.Discard; + displaypp.BackBufferCount = 1; + displaypp.BackBufferFormat = currentmode.Format; + displaypp.BackBufferWidth = rendertarget.ClientSize.Width; + displaypp.BackBufferHeight = rendertarget.ClientSize.Height; + displaypp.EnableAutoDepthStencil = true; + displaypp.AutoDepthStencilFormat = Format.D16; + displaypp.Multisample = MultisampleType.None; + displaypp.PresentationInterval = PresentInterval.Immediate; + + // Return result + return displaypp; + } + + #endregion + + #region ================== Resetting + + // This registers a resource + internal void RegisterResource(ID3DResource res) + { + // Add resource + resources.Add(res, res); + } + + // This unregisters a resource + internal void UnregisterResource(ID3DResource res) + { + // Remove resource + resources.Remove(res); + } + + // This resets the device and returns true on success + internal bool Reset() + { + PresentParameters displaypp; + + // Test the cooperative level + Result coopresult = device.TestCooperativeLevel(); + + // Can we reset? + //if(coopresult.Name != "D3DERR_DEVICENOTRESET") + { + // Unload all Direct3D resources + foreach (ID3DResource res in resources.Values) res.UnloadResource(); + + // Lose backbuffers + if (backbuffer != null) backbuffer.Dispose(); + if (depthbuffer != null) depthbuffer.Dispose(); + backbuffer = null; + depthbuffer = null; + + // Make present parameters + displaypp = CreatePresentParameters(adapter); + + try + { + // Reset the device + device.Reset(displaypp); + } + catch (Exception) + { + // Failed to re-initialize + return false; + } + + // Keep a reference to the original buffers + backbuffer = device.GetBackBuffer(0, 0); + depthbuffer = device.DepthStencilSurface; + + // Get the viewport + viewport = device.Viewport; + + // Reload all Direct3D resources + foreach (ID3DResource res in resources.Values) res.ReloadResource(); + + // Re-apply settings + SetupSettings(); + + // Success + return true; + } + /* + else + { + // Failed + return false; + } + */ + } + + #endregion + + #region ================== Rendering + + // This begins a drawing session + public bool StartRendering(bool clear, Color4 backcolor, Surface target, Surface depthbuffer) + { + // Check if we can render + if (CheckAvailability()) + { + // Set rendertarget + device.DepthStencilSurface = depthbuffer; + device.SetRenderTarget(0, target); + + // Clear the screen + if (clear) + { + if (depthbuffer != null) + device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, backcolor, 1f, 0); + else + device.Clear(ClearFlags.Target, backcolor, 1f, 0); + } + + // Ready to render + device.BeginScene(); + return true; + } + else + { + // Minimized, you cannot see anything + return false; + } + } + + // This clears a target + public void ClearRendertarget(Color4 backcolor, Surface target, Surface depthbuffer) + { + // Set rendertarget + device.DepthStencilSurface = depthbuffer; + device.SetRenderTarget(0, target); + + if (depthbuffer != null) + device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, backcolor, 1f, 0); + else + device.Clear(ClearFlags.Target, backcolor, 1f, 0); + } + + // This ends a drawing session + public void FinishRendering() + { + try + { + // Done + device.EndScene(); + } + // Errors are not a problem here + catch (Exception) { } + } + + // This presents what has been drawn + public void Present() + { + try + { + device.Present(); + } + // Errors are not a problem here + catch (Exception) { } + } + + // This checks if we can use the hardware at this moment + public bool CheckAvailability() + { + // When minimized, the hardware is not available + if (General.MainWindow.WindowState != FormWindowState.Minimized) + { + // Test the cooperative level + Result coopresult = device.TestCooperativeLevel(); + + // Check if device must be reset + if (!coopresult.IsSuccess) + { + // Should we reset? + if (coopresult.Name == "D3DERR_DEVICENOTRESET") + { + // Device is lost and must be reset now + Reset(); + } + + // Impossible to render at this point + return false; + } + else + { + // Read to go! + return true; + } + } + else + { + // Minimized + return false; + } + } + + #endregion + + #region ================== Tools + + // Make a color from ARGB + public static int ARGB(float a, float r, float g, float b) + { + return Color.FromArgb((int)(a * 255f), (int)(r * 255f), (int)(g * 255f), (int)(b * 255f)).ToArgb(); + } + + // Make a color from RGB + public static int RGB(int r, int g, int b) + { + return Color.FromArgb(255, r, g, b).ToArgb(); + } + + // This makes a Vector3 from Vector3D + public static Vector3 V3(Vector3D v3d) + { + return new Vector3(v3d.x, v3d.y, v3d.z); + } + + // This makes a Vector3D from Vector3 + public static Vector3D V3D(Vector3 v3) + { + return new Vector3D(v3.X, v3.Y, v3.Z); + } + + // This makes a Vector2 from Vector2D + public static Vector2 V2(Vector2D v2d) + { + return new Vector2(v2d.x, v2d.y); + } + + // This makes a Vector2D from Vector2 + public static Vector2D V2D(Vector2 v2) + { + return new Vector2D(v2.X, v2.Y); + } + + #endregion + } +} diff --git a/Source/Core/Rendering/D3DShader.cs b/Source/Core/Rendering/D3DShader.cs new file mode 100644 index 0000000..1518177 --- /dev/null +++ b/Source/Core/Rendering/D3DShader.cs @@ -0,0 +1,187 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using System.Drawing; +using System.ComponentModel; +using CodeImp.DoomBuilder.Map; +using SlimDX.Direct3D9; +using SlimDX; +using CodeImp.DoomBuilder.Geometry; +using System.Drawing.Imaging; + +#endregion + +namespace CodeImp.DoomBuilder.Rendering +{ + internal abstract class D3DShader + { + #region ================== Constants + + #endregion + + #region ================== Variables + + // The manager + protected ShaderManager manager; + + // The effect + protected Effect effect; + + // The vertex declaration + protected VertexDeclaration vertexdecl; + + // Disposing + protected bool isdisposed = false; + + #endregion + + #region ================== Properties + + // Disposing + public bool IsDisposed { get { return isdisposed; } } + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + public D3DShader(ShaderManager manager) + { + // Initialize + this.manager = manager; + + // We have no destructor + GC.SuppressFinalize(this); + } + + // Disposer + public virtual void Dispose() + { + // Not already disposed? + if (!isdisposed) + { + // Clean up + manager = null; + if (effect != null) effect.Dispose(); + vertexdecl.Dispose(); + + // Done + isdisposed = true; + } + } + + #endregion + + #region ================== Methods + + // This loads an effect + protected Effect LoadEffect(string fxfile) + { + Effect fx; + string errors; + Stream fxdata; + + // Return null when not using shaders + if (!manager.Enabled) return null; + + // Load the resource + fxdata = General.ThisAssembly.GetManifestResourceStream("CodeImp.DoomBuilder.Resources." + fxfile); + fxdata.Seek(0, SeekOrigin.Begin); + + try + { + // Compile effect + fx = Effect.FromStream(General.Map.Graphics.Device, fxdata, null, null, null, ShaderFlags.None, null, out errors); + if (!string.IsNullOrEmpty(errors)) + { + throw new Exception("Errors in effect file " + fxfile + ": " + errors); + } + } + catch (Exception) + { + // Compiling failed, try with debug information + try + { + // Compile effect + fx = Effect.FromStream(General.Map.Graphics.Device, fxdata, null, null, null, ShaderFlags.Debug, null, out errors); + if (!string.IsNullOrEmpty(errors)) + { + throw new Exception("Errors in effect file " + fxfile + ": " + errors); + } + } + catch (Exception e) + { + // No debug information, just crash + throw new Exception(e.GetType().Name + " while loading effect " + fxfile + ": " + e.Message); + } + } + + fxdata.Dispose(); + + // Set the technique to use + fx.Technique = manager.ShaderTechnique; + + // Return result + return fx; + } + + // This applies the shader + public void Begin() + { + // Set vertex declaration + General.Map.Graphics.Device.VertexDeclaration = vertexdecl; + + // Set effect + if (manager.Enabled) effect.Begin(FX.DoNotSaveState); + } + + // This begins a pass + public virtual void BeginPass(int index) + { + if (manager.Enabled) effect.BeginPass(index); + } + + // This ends a pass + public void EndPass() + { + if (manager.Enabled) effect.EndPass(); + } + + // This ends te shader + public void End() + { + if (manager.Enabled) effect.End(); + } + + // This applies properties during a pass + public void ApplySettings() + { + if (manager.Enabled) effect.CommitChanges(); + } + + #endregion + } +} diff --git a/Source/Core/Rendering/Display2DShader.cs b/Source/Core/Rendering/Display2DShader.cs new file mode 100644 index 0000000..8c1a2ab --- /dev/null +++ b/Source/Core/Rendering/Display2DShader.cs @@ -0,0 +1,189 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using System.Drawing; +using System.ComponentModel; +using CodeImp.DoomBuilder.Map; +using SlimDX.Direct3D9; +using SlimDX; +using CodeImp.DoomBuilder.Geometry; +using System.Drawing.Imaging; + +#endregion + +namespace CodeImp.DoomBuilder.Rendering +{ + internal sealed class Display2DShader : D3DShader + { + #region ================== Variables + + // Property handlers + private EffectHandle texture1; + private EffectHandle rendersettings; + private EffectHandle transformsettings; + private EffectHandle filtersettings; + + #endregion + + #region ================== Properties + + public Texture Texture1 { set { if (manager.Enabled) effect.SetTexture(texture1, value); } } + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + public Display2DShader(ShaderManager manager) : base(manager) + { + // Load effect from file + effect = LoadEffect("display2d.fx"); + + // Get the property handlers from effect + if (effect != null) + { + texture1 = effect.GetParameter(null, "texture1"); + rendersettings = effect.GetParameter(null, "rendersettings"); + transformsettings = effect.GetParameter(null, "transformsettings"); + filtersettings = effect.GetParameter(null, "filtersettings"); + } + + // Initialize world vertex declaration + VertexElement[] elements = new VertexElement[] + { + new VertexElement(0, 0, DeclarationType.Float3, DeclarationMethod.Default, DeclarationUsage.Position, 0), + new VertexElement(0, 12, DeclarationType.Color, DeclarationMethod.Default, DeclarationUsage.Color, 0), + new VertexElement(0, 16, DeclarationType.Float2, DeclarationMethod.Default, DeclarationUsage.TextureCoordinate, 0), + VertexElement.VertexDeclarationEnd + }; + vertexdecl = new VertexDeclaration(General.Map.Graphics.Device, elements); + + // We have no destructor + GC.SuppressFinalize(this); + } + + // Disposer + public override void Dispose() + { + // Not already disposed? + if (!isdisposed) + { + // Clean up + if (texture1 != null) texture1.Dispose(); + if (rendersettings != null) rendersettings.Dispose(); + if (transformsettings != null) transformsettings.Dispose(); + if (filtersettings != null) filtersettings.Dispose(); + + // Done + base.Dispose(); + } + } + + #endregion + + #region ================== Methods + + // This sets the settings + public void SetSettings(float texelx, float texely, float fsaafactor, float alpha, bool bilinear) + { + if (manager.Enabled) + { + Vector4 values = new Vector4(texelx, texely, fsaafactor, alpha); + effect.SetValue(rendersettings, values); + Matrix world = manager.D3DDevice.Device.GetTransform(TransformState.World); + Matrix view = manager.D3DDevice.Device.GetTransform(TransformState.View); + effect.SetValue(transformsettings, Matrix.Multiply(world, view)); + TextureFilter filter = TextureFilter.Point; + if (bilinear) filter = TextureFilter.Linear; + effect.SetValue(filtersettings, (int)filter); + } + } + + // This sets up the render pipeline + public override void BeginPass(int index) + { + Device device = manager.D3DDevice.Device; + + if (!manager.Enabled) + { + // Sampler settings + if (General.Settings.ClassicBilinear) + { + device.SetSamplerState(0, SamplerState.MagFilter, TextureFilter.Linear); + device.SetSamplerState(0, SamplerState.MinFilter, TextureFilter.Linear); + device.SetSamplerState(0, SamplerState.MipFilter, TextureFilter.Linear); + device.SetSamplerState(0, SamplerState.MipMapLodBias, 0f); + } + else + { + device.SetSamplerState(0, SamplerState.MagFilter, TextureFilter.Point); + device.SetSamplerState(0, SamplerState.MinFilter, TextureFilter.Point); + device.SetSamplerState(0, SamplerState.MipFilter, TextureFilter.Point); + device.SetSamplerState(0, SamplerState.MipMapLodBias, 0f); + } + + // Texture addressing + device.SetSamplerState(0, SamplerState.AddressU, TextureAddress.Wrap); + device.SetSamplerState(0, SamplerState.AddressV, TextureAddress.Wrap); + device.SetSamplerState(0, SamplerState.AddressW, TextureAddress.Wrap); + + // First texture stage + device.SetTextureStageState(0, TextureStage.ColorOperation, TextureOperation.Modulate); + device.SetTextureStageState(0, TextureStage.ColorArg1, TextureArgument.Texture); + device.SetTextureStageState(0, TextureStage.ColorArg2, TextureArgument.Diffuse); + device.SetTextureStageState(0, TextureStage.ResultArg, TextureArgument.Current); + device.SetTextureStageState(0, TextureStage.TexCoordIndex, 0); + + // Second texture stage + device.SetTextureStageState(1, TextureStage.ColorOperation, TextureOperation.Modulate); + device.SetTextureStageState(1, TextureStage.ColorArg1, TextureArgument.Current); + device.SetTextureStageState(1, TextureStage.ColorArg2, TextureArgument.TFactor); + device.SetTextureStageState(1, TextureStage.ResultArg, TextureArgument.Current); + device.SetTextureStageState(1, TextureStage.TexCoordIndex, 0); + + // No more further stages + device.SetTextureStageState(2, TextureStage.ColorOperation, TextureOperation.Disable); + + // First alpha stage + device.SetTextureStageState(0, TextureStage.AlphaOperation, TextureOperation.Modulate); + device.SetTextureStageState(0, TextureStage.AlphaArg1, TextureArgument.Texture); + device.SetTextureStageState(0, TextureStage.AlphaArg2, TextureArgument.Diffuse); + + // Second alpha stage + device.SetTextureStageState(1, TextureStage.AlphaOperation, TextureOperation.Modulate); + device.SetTextureStageState(1, TextureStage.AlphaArg1, TextureArgument.Current); + device.SetTextureStageState(1, TextureStage.AlphaArg2, TextureArgument.TFactor); + + // No more further stages + device.SetTextureStageState(2, TextureStage.AlphaOperation, TextureOperation.Disable); + } + + base.BeginPass(index); + } + + #endregion + } +} diff --git a/Source/Core/Rendering/FlatQuad.cs b/Source/Core/Rendering/FlatQuad.cs new file mode 100644 index 0000000..4b5ad04 --- /dev/null +++ b/Source/Core/Rendering/FlatQuad.cs @@ -0,0 +1,251 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Drawing; +using SlimDX.Direct3D9; +using SlimDX; + +#endregion + +namespace CodeImp.DoomBuilder.Rendering +{ + // FlatQuad + internal class FlatQuad + { + #region ================== Variables + + // Vertices + private FlatVertex[] vertices; + private PrimitiveType type; + private int numvertices; + + #endregion + + #region ================== Properties + + public FlatVertex[] Vertices { get { return vertices; } } + public PrimitiveType Type { get { return type; } } + + #endregion + + #region ================== Constructors + + // Constructor + public FlatQuad(PrimitiveType type, float left, float top, float right, float bottom) + { + // Initialize + Initialize(type); + + // Set coordinates + if (type == PrimitiveType.TriangleList) + SetTriangleListCoordinates(left, top, right, bottom, 0f, 0f, 1f, 1f); + else if (type == PrimitiveType.TriangleStrip) + SetTriangleStripCoordinates(left, top, right, bottom, 0f, 0f, 1f, 1f); + + // We have no destructor + GC.SuppressFinalize(this); + } + + // Constructor + public FlatQuad(PrimitiveType type, float left, float top, float right, float bottom, float twidth, float theight) + { + float twd, thd; + + // Initialize + Initialize(type); + + // Determine texture size dividers + twd = 1f / twidth; + thd = 1f / theight; + + // Set coordinates + if (type == PrimitiveType.TriangleList) + SetTriangleListCoordinates(left, top, right, bottom, twd, thd, 1f - twd, 1f - thd); + else if (type == PrimitiveType.TriangleStrip) + SetTriangleStripCoordinates(left, top, right, bottom, twd, thd, 1f - twd, 1f - thd); + + // We have no destructor + GC.SuppressFinalize(this); + } + + // Constructor + public FlatQuad(PrimitiveType type, RectangleF pos, float tl, float tt, float tr, float tb) + { + // Initialize + Initialize(type); + + // Set coordinates + if (type == PrimitiveType.TriangleList) + SetTriangleListCoordinates(pos.Left, pos.Top, pos.Right, pos.Bottom, tl, tt, tr, tb); + else if (type == PrimitiveType.TriangleStrip) + SetTriangleStripCoordinates(pos.Left, pos.Top, pos.Right, pos.Bottom, tl, tt, tr, tb); + + // We have no destructor + GC.SuppressFinalize(this); + } + + // Constructor + public FlatQuad(PrimitiveType type, float left, float top, float right, float bottom, float tl, float tt, float tr, float tb) + { + // Initialize + Initialize(type); + + // Set coordinates + if (type == PrimitiveType.TriangleList) + SetTriangleListCoordinates(left, top, right, bottom, tl, tt, tr, tb); + else if (type == PrimitiveType.TriangleStrip) + SetTriangleStripCoordinates(left, top, right, bottom, tl, tt, tr, tb); + + // We have no destructor + GC.SuppressFinalize(this); + } + + #endregion + + #region ================== Methods + + // This sets the color on all vertices + public void SetColors(int color) + { + // Go for all vertices to set the color + for (int i = 0; i < numvertices; i++) + vertices[i].c = color; + } + + // This sets the color on all vertices + public void SetColors(int clt, int crt, int clb, int crb) + { + // Determine polygon type + if (type == PrimitiveType.TriangleList) + { + // Go for all vertices to set the color + vertices[0].c = clt; + vertices[1].c = crt; + vertices[2].c = clb; + vertices[3].c = clb; + vertices[4].c = crt; + vertices[5].c = crb; + } + else if (type == PrimitiveType.TriangleStrip) + { + // Go for all vertices to set the color + vertices[0].c = clt; + vertices[1].c = crt; + vertices[2].c = clb; + vertices[3].c = crb; + } + } + + // This applies coordinates for TriangleList type + private void SetTriangleListCoordinates(float vl, float vt, float vr, float vb, + float tl, float tt, float tr, float tb) + { + // Setup coordinates + vertices[0].x = vl; + vertices[0].y = vt; + vertices[1].x = vr; + vertices[1].y = vt; + vertices[2].x = vl; + vertices[2].y = vb; + vertices[3].x = vl; + vertices[3].y = vb; + vertices[4].x = vr; + vertices[4].y = vt; + vertices[5].x = vr; + vertices[5].y = vb; + + // Set texture coordinates + vertices[0].u = tl; + vertices[0].v = tt; + vertices[1].u = tr; + vertices[1].v = tt; + vertices[2].u = tl; + vertices[2].v = tb; + vertices[3].u = tl; + vertices[3].v = tb; + vertices[4].u = tr; + vertices[4].v = tt; + vertices[5].u = tr; + vertices[5].v = tb; + } + + // This applies coordinates for TriangleStrip type + private void SetTriangleStripCoordinates(float vl, float vt, float vr, float vb, + float tl, float tt, float tr, float tb) + { + // Setup coordinates + vertices[0].x = vl; + vertices[0].y = vt; + vertices[1].x = vr; + vertices[1].y = vt; + vertices[2].x = vl; + vertices[2].y = vb; + vertices[3].x = vr; + vertices[3].y = vb; + + // Set texture coordinates + vertices[0].u = tl; + vertices[0].v = tt; + vertices[1].u = tr; + vertices[1].v = tt; + vertices[2].u = tl; + vertices[2].v = tb; + vertices[3].u = tr; + vertices[3].v = tb; + } + + // This initializes vertices to default values + private void Initialize(PrimitiveType type) + { + // Determine primitive type + this.type = type; + + // Determine number of vertices + if (type == PrimitiveType.TriangleList) + numvertices = 6; + else if (type == PrimitiveType.TriangleStrip) + numvertices = 4; + else + throw new NotSupportedException("Unsupported PrimitiveType"); + + // Make the array + vertices = new FlatVertex[numvertices]; + + // Go for all vertices + for (int i = 0; i < numvertices; i++) + { + // Initialize to defaults + vertices[i].c = -1; + } + } + + #endregion + + #region ================== Rendering + + // This renders the quad + public void Render(D3DDevice device) + { + // Render the quad + device.Device.DrawUserPrimitives(type, 0, 2, vertices); + } + + #endregion + } +} diff --git a/Source/Core/Rendering/FlatVertex.cs b/Source/Core/Rendering/FlatVertex.cs new file mode 100644 index 0000000..1704f86 --- /dev/null +++ b/Source/Core/Rendering/FlatVertex.cs @@ -0,0 +1,53 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using System.Drawing; +using System.ComponentModel; +using CodeImp.DoomBuilder.Map; +using SlimDX.Direct3D9; +using SlimDX; +using CodeImp.DoomBuilder.Geometry; +using System.Drawing.Imaging; + +#endregion + +namespace CodeImp.DoomBuilder.Rendering +{ + // FlatVertex + public struct FlatVertex + { + // Vertex format + public static readonly int Stride = 6 * 4; + + // Members + public float x; + public float y; + public float z; + public int c; + public float u; + public float v; + } +} diff --git a/Source/Core/Rendering/ID3DResource.cs b/Source/Core/Rendering/ID3DResource.cs new file mode 100644 index 0000000..10309f0 --- /dev/null +++ b/Source/Core/Rendering/ID3DResource.cs @@ -0,0 +1,45 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using System.Drawing; +using SlimDX.Direct3D9; +using System.ComponentModel; +using CodeImp.DoomBuilder.Geometry; +using SlimDX; + +#endregion + +namespace CodeImp.DoomBuilder.Rendering +{ + internal interface ID3DResource + { + // This is used to unload the resouce + void UnloadResource(); + + // This is used to reload the resource + void ReloadResource(); + } +} diff --git a/Source/Core/Rendering/IRenderer2D.cs b/Source/Core/Rendering/IRenderer2D.cs new file mode 100644 index 0000000..a6a9932 --- /dev/null +++ b/Source/Core/Rendering/IRenderer2D.cs @@ -0,0 +1,89 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using System.Drawing; +using System.ComponentModel; +using CodeImp.DoomBuilder.Map; +using SlimDX.Direct3D9; +using SlimDX; +using CodeImp.DoomBuilder.Geometry; +using System.Drawing.Imaging; +using CodeImp.DoomBuilder.Data; +using CodeImp.DoomBuilder.Editing; + +#endregion + +namespace CodeImp.DoomBuilder.Rendering +{ + public interface IRenderer2D + { + // Properties + float OffsetX { get; } + float OffsetY { get; } + float TranslateX { get; } + float TranslateY { get; } + float Scale { get; } + int VertexSize { get; } + ViewMode ViewMode { get; } + + // View methods + Vector2D DisplayToMap(Vector2D mousepos); + Vector2D MapToDisplay(Vector2D mappos); + + // Color methods + PixelColor DetermineLinedefColor(Linedef l); + PixelColor DetermineThingColor(Thing t); + int DetermineVertexColor(Vertex v); + int CalculateBrightness(int level); + + // Rendering management methods + bool StartPlotter(bool clear); + bool StartThings(bool clear); + bool StartOverlay(bool clear); + void Finish(); + void SetPresentation(Presentation present); + void Present(); + + // Drawing methods + void PlotLine(Vector2D start, Vector2D end, PixelColor c); + void PlotLinedef(Linedef l, PixelColor c); + void PlotLinedefSet(ICollection linedefs); + void PlotSector(Sector s); + void PlotSector(Sector s, PixelColor c); + void PlotVertex(Vertex v, int colorindex); + void PlotVertexAt(Vector2D v, int colorindex); + void PlotVerticesSet(ICollection vertices); + void RenderThing(Thing t, PixelColor c, float alpha); + void RenderThingSet(ICollection things, float alpha); + void RenderRectangle(RectangleF rect, float bordersize, PixelColor c, bool transformrect); + void RenderRectangleFilled(RectangleF rect, PixelColor c, bool transformrect); + void RenderRectangleFilled(RectangleF rect, PixelColor c, bool transformrect, ImageData texture); + void RenderLine(Vector2D start, Vector2D end, float thickness, PixelColor c, bool transformcoords); + void RenderText(TextLabel text); + void RenderGeometry(FlatVertex[] vertices, ImageData texture, bool transformcoords); + void RedrawSurface(); + } +} diff --git a/Source/Core/Rendering/IRenderer3D.cs b/Source/Core/Rendering/IRenderer3D.cs new file mode 100644 index 0000000..83e7f79 --- /dev/null +++ b/Source/Core/Rendering/IRenderer3D.cs @@ -0,0 +1,69 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using System.Drawing; +using System.ComponentModel; +using CodeImp.DoomBuilder.Map; +using SlimDX.Direct3D9; +using SlimDX; +using CodeImp.DoomBuilder.Geometry; +using System.Drawing.Imaging; +using CodeImp.DoomBuilder.Data; +using CodeImp.DoomBuilder.Editing; +using CodeImp.DoomBuilder.VisualModes; + +#endregion + +namespace CodeImp.DoomBuilder.Rendering +{ + public interface IRenderer3D + { + // Properties + ProjectedFrustum2D Frustum2D { get; } + bool DrawThingCages { get; set; } + bool ShowSelection { get; set; } + bool ShowHighlight { get; set; } + bool ShowLightOnly { get; set; } // villsa + + // General methods + void PositionAndLookAt(Vector3D pos, Vector3D lookat); + + // Presenting methods + void Finish(); + bool Start(); + void StartGeometry(); + void FinishGeometry(); + + // Rendering methods + int CalculateBrightness(int level); + void SetHighlightedObject(IVisualPickable obj); + void AddSectorGeometry(VisualGeometry g); + void AddThingGeometry(VisualThing t); + void RenderCrosshair(); + void SetFogMode(bool usefog); + void SetCrosshairBusy(bool busy); + } +} diff --git a/Source/Core/Rendering/PixelColor.cs b/Source/Core/Rendering/PixelColor.cs new file mode 100644 index 0000000..d103a4e --- /dev/null +++ b/Source/Core/Rendering/PixelColor.cs @@ -0,0 +1,218 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Reflection; +using System.Drawing; +using SlimDX.Direct3D9; +using SlimDX; + +#endregion + +namespace CodeImp.DoomBuilder.Rendering +{ + public struct PixelColor + { + #region ================== Constants + + public const float BYTE_TO_FLOAT = 0.00392156862745098f; + + #endregion + + #region ================== Statics + + public static readonly PixelColor Transparent = new PixelColor(0, 0, 0, 0); + + #endregion + + #region ================== Variables + + // Members + public byte b; + public byte g; + public byte r; + public byte a; + + #endregion + + #region ================== Constructors + + // Constructor + public PixelColor(byte a, byte r, byte g, byte b) + { + // Initialize + this.a = a; + this.r = r; + this.g = g; + this.b = b; + } + + // Constructor + public PixelColor(PixelColor p, byte a) + { + // Initialize + this.a = a; + this.r = p.r; + this.g = p.g; + this.b = p.b; + } + + #endregion + + #region ================== Methods + + // Construct from color + public static PixelColor FromColor(Color c) + { + return new PixelColor(c.A, c.R, c.G, c.B); + } + + // Construct from int + public static PixelColor FromInt(int c) + { + return FromColor(Color.FromArgb(c)); + } + + // Construct from Hex + public static PixelColor FromHex(string h) + { + // Add FF for Alpha value + int value = int.Parse("FF" + h, System.Globalization.NumberStyles.HexNumber); + return FromInt(value); + } + + // Return the inverse color + public PixelColor Inverse() + { + return new PixelColor((byte)(255 - a), (byte)(255 - r), (byte)(255 - g), (byte)(255 - b)); + } + + // Return the inverse color, but keep same alpha + public PixelColor InverseKeepAlpha() + { + return new PixelColor(a, (byte)(255 - r), (byte)(255 - g), (byte)(255 - b)); + } + + // To int + public int ToInt() + { + return Color.FromArgb(a, r, g, b).ToArgb(); + } + + // To Hex + public string ToHex() + { + Color c = Color.FromArgb(a, r, g, b); + string h = c.R.ToString("X2") + c.G.ToString("X2") + c.B.ToString("X2"); + return h; + + } + + // To Color + public Color ToColor() + { + return Color.FromArgb(a, r, g, b); + } + + // To ColorRef (alpha-less) + public int ToColorRef() + { + return ((int)r + ((int)b << 16) + ((int)g << 8)); + } + + // To ColorValue + public Color4 ToColorValue() + { + return new Color4((float)a * BYTE_TO_FLOAT, + (float)r * BYTE_TO_FLOAT, + (float)g * BYTE_TO_FLOAT, + (float)b * BYTE_TO_FLOAT); + } + + // To ColorValue + public Color4 ToColorValue(float withalpha) + { + return new Color4(withalpha, + (float)r * BYTE_TO_FLOAT, + (float)g * BYTE_TO_FLOAT, + (float)b * BYTE_TO_FLOAT); + } + + // This returns a new PixelColor with adjusted alpha + public PixelColor WithAlpha(byte a) + { + return new PixelColor(this, a); + } + + // This blends two colors with respect to alpha + public PixelColor Blend(PixelColor a, PixelColor b) + { + PixelColor c = new PixelColor(); + float ba; + + ba = (float)a.a * BYTE_TO_FLOAT; + c.r = (byte)((float)a.r * (1f - ba) + (float)b.r * ba); + c.g = (byte)((float)a.g * (1f - ba) + (float)b.g * ba); + c.b = (byte)((float)a.b * (1f - ba) + (float)b.b * ba); + c.a = (byte)((float)a.a * (1f - ba) + ba); + + return c; + } + + // This modulates two colors + public static PixelColor Modulate(PixelColor a, PixelColor b) + { + float aa = (float)a.a * BYTE_TO_FLOAT; + float ar = (float)a.r * BYTE_TO_FLOAT; + float ag = (float)a.g * BYTE_TO_FLOAT; + float ab = (float)a.b * BYTE_TO_FLOAT; + float ba = (float)b.a * BYTE_TO_FLOAT; + float br = (float)b.r * BYTE_TO_FLOAT; + float bg = (float)b.g * BYTE_TO_FLOAT; + float bb = (float)b.b * BYTE_TO_FLOAT; + PixelColor c = new PixelColor(); + c.a = (byte)((aa * ba) * 255.0f); + c.r = (byte)((ar * br) * 255.0f); + c.g = (byte)((ag * bg) * 255.0f); + c.b = (byte)((ab * bb) * 255.0f); + return c; + } + + public static PixelColor Modulate(PixelColor a, float multiplier) + { + float aa = (float)a.a * BYTE_TO_FLOAT; + float ar = (float)a.r * BYTE_TO_FLOAT; + float ag = (float)a.g * BYTE_TO_FLOAT; + float ab = (float)a.b * BYTE_TO_FLOAT; + float x = 255.0f * BYTE_TO_FLOAT; + + PixelColor c = new PixelColor(); + c.a = (byte)(((aa * x) * 255.0f) * multiplier); + c.r = (byte)(((ar * x) * 255.0f) * multiplier); + c.g = (byte)(((ag * x) * 255.0f) * multiplier); + c.b = (byte)(((ab * x) * 255.0f) * multiplier); + return c; + } + + #endregion + } +} diff --git a/Source/Core/Rendering/PixelColorBlock.cs b/Source/Core/Rendering/PixelColorBlock.cs new file mode 100644 index 0000000..83c9f77 --- /dev/null +++ b/Source/Core/Rendering/PixelColorBlock.cs @@ -0,0 +1,92 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Reflection; +using System.Drawing; +using SlimDX.Direct3D9; +using System.Runtime.InteropServices; + +#endregion + +namespace CodeImp.DoomBuilder.Rendering +{ + public unsafe class PixelColorBlock + { + #region ================== Variables + + private int width; + private int height; + private int memorysize; + private PixelColor* memory; + + #endregion + + #region ================== Properties + + public int Width { get { return width; } } + public int Height { get { return height; } } + public int Length { get { return memorysize; } } + public PixelColor this[int index] { get { return memory[index]; } set { memory[index] = value; } } + public PixelColor* Pointer { get { return memory; } } + + #endregion + + #region ================== Constructor / Destructor + + // Constructor + public PixelColorBlock(int width, int height) + { + // Check input + if ((width <= 0) || (height <= 0)) throw new ArgumentException("Cannot allocate a memory block of zero size!"); + + // Initialize + this.width = width; + this.height = height; + this.memorysize = width * height * sizeof(PixelColor); + this.memory = (PixelColor*)Marshal.AllocCoTaskMem(memorysize); + if (this.memory == (PixelColor*)0) throw new OutOfMemoryException(); + GC.AddMemoryPressure(memorysize); + } + + // Destructor + ~PixelColorBlock() + { + // Terminate + Marshal.FreeCoTaskMem(new IntPtr(memory)); + GC.RemoveMemoryPressure(memorysize); + memorysize = 0; + } + + #endregion + + #region ================== Methods + + // This clears the memory black + public void Clear() + { + if (memorysize > 0) General.ZeroMemory(new IntPtr(memory), (int)memorysize); + } + + #endregion + } +} diff --git a/Source/Core/Rendering/Plotter.cs b/Source/Core/Rendering/Plotter.cs new file mode 100644 index 0000000..6905841 --- /dev/null +++ b/Source/Core/Rendering/Plotter.cs @@ -0,0 +1,349 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using System.Drawing; +using System.ComponentModel; +using CodeImp.DoomBuilder.Map; +using SlimDX.Direct3D9; +using SlimDX; +using CodeImp.DoomBuilder.Geometry; +using System.Drawing.Imaging; + +#endregion + +namespace CodeImp.DoomBuilder.Rendering +{ + internal unsafe sealed class Plotter + { + #region ================== Constants + + #endregion + + #region ================== Variables + + // Memory + private PixelColor* pixels; + private int width; + private int height; + private int visiblewidth; + private int visibleheight; + + #endregion + + #region ================== Properties + + public int VisibleWidth { get { return visiblewidth; } } + public int VisibleHeight { get { return visibleheight; } } + public int Width { get { return width; } } + public int Height { get { return height; } } + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + public Plotter(PixelColor* pixels, int width, int height, int visiblewidth, int visibleheight) + { + // Initialize + this.pixels = pixels; + this.width = width; + this.height = height; + this.visiblewidth = width; + this.visibleheight = height; + + // We have no destructor + GC.SuppressFinalize(this); + } + + #endregion + + #region ================== Pixel Rendering + + // This clears all pixels black + public void Clear() + { + // Clear memory + General.ZeroMemory(new IntPtr(pixels), width * height * sizeof(PixelColor)); + } + + // This draws a pixel normally + public void DrawPixelSolid(int x, int y, ref PixelColor c) + { + // Draw pixel when within range + if ((x >= 0) && (x < visiblewidth) && (y >= 0) && (y < visibleheight)) + pixels[y * width + x] = c; + } + + // This draws a pixel normally + public void DrawVertexSolid(int x, int y, int size, ref PixelColor c, ref PixelColor l, ref PixelColor d) + { + int x1 = x - size; + int x2 = x + size; + int y1 = y - size; + int y2 = y + size; + int xp, yp; + + // Do unchecked? + if ((x1 >= 0) && (x2 < visiblewidth) && (y1 >= 0) && (y2 < visibleheight)) + { + // Filled square + for (yp = y1; yp <= y2; yp++) + for (xp = x1; xp <= x2; xp++) + pixels[yp * width + xp] = c; + + // Vertical edges + for (yp = y1 + 1; yp <= y2 - 1; yp++) + { + pixels[yp * width + x1] = l; + pixels[yp * width + x2] = d; + } + + // Horizontal edges + for (xp = x1 + 1; xp <= x2 - 1; xp++) + { + pixels[y1 * width + xp] = l; + pixels[y2 * width + xp] = d; + } + + // Corners + pixels[y2 * width + x2] = d; + pixels[y1 * width + x1] = l; + } + /* + else + { + // Filled square + for(yp = y - size; yp <= y + size; yp++) + for(xp = x - size; xp <= x + size; xp++) + DrawPixelSolid(xp, yp, c); + + // Vertical edges + for(yp = y - size + 1; yp <= y + size - 1; yp++) + { + DrawPixelSolid(x - size, yp, l); + DrawPixelSolid(x + size, yp, d); + } + + // Horizontal edges + for(xp = x - size + 1; xp <= x + size - 1; xp++) + { + DrawPixelSolid(xp, y - size, l); + DrawPixelSolid(xp, y + size, d); + } + + // Corners + DrawPixelSolid(x + size, y + size, d); + DrawPixelSolid(x - size, y - size, l); + } + */ + } + + // This draws a dotted grid line horizontally + public void DrawGridLineH(int y, int x1, int x2, ref PixelColor c) + { + int numpixels = visiblewidth >> 1; + int offset = y & 0x01; + int ywidth = y * width; + x1 = General.Clamp(x1 >> 1, 0, numpixels - 1); + x2 = General.Clamp(x2 >> 1, 0, numpixels - 1); + + if ((y >= 0) && (y < height)) + { + // Draw all pixels on this line + for (int i = x1; i < x2; i++) pixels[ywidth + ((i << 1) | offset)] = c; + } + } + + // This draws a dotted grid line vertically + public void DrawGridLineV(int x, int y1, int y2, ref PixelColor c) + { + int numpixels = visibleheight >> 1; + int offset = x & 0x01; + y1 = General.Clamp(y1 >> 1, 0, numpixels - 1); + y2 = General.Clamp(y2 >> 1, 0, numpixels - 1); + + if ((x >= 0) && (x < width)) + { + // Draw all pixels on this line + for (int i = y1; i < y2; i++) pixels[((i << 1) | offset) * width + x] = c; + } + } + + // This draws a pixel alpha blended + public void DrawPixelAlpha(int x, int y, ref PixelColor c) + { + float a; + + // Draw only when within range + if ((x >= 0) && (x < visiblewidth) && (y >= 0) && (y < visibleheight)) + { + // Get the target pixel + PixelColor* p = pixels + (y * width + x); + + // Not drawn on target yet? + if (*(int*)p == 0) + { + // Simply apply color to pixel + *p = c; + } + else + { + // Blend with pixel + a = (float)c.a * 0.003921568627450980392156862745098f; + if ((int)p->a + (int)c.a > 255) p->a = 255; else p->a += c.a; + p->r = (byte)((float)p->r * (1f - a) + (float)c.r * a); + p->g = (byte)((float)p->g * (1f - a) + (float)c.g * a); + p->b = (byte)((float)p->b * (1f - a) + (float)c.b * a); + } + } + } + + // This draws a line normally + // See: http://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm + public void DrawLineSolid(int x1, int y1, int x2, int y2, ref PixelColor c) + { + int i; + + // Check if the line is outside the screen for sure. + // This is quickly done by checking in which area both points are. When this + // is above, below, right or left of the screen, then skip drawing the line. + if (((x1 < 0) && (x2 < 0)) || + ((x1 > visiblewidth) && (x2 > visiblewidth)) || + ((y1 < 0) && (y2 < 0)) || + ((y1 > visibleheight) && (y2 > visibleheight))) return; + + // Distance of the line + int dx = x2 - x1; + int dy = y2 - y1; + + // Positive (absolute) distance + int dxabs = Math.Abs(dx); + int dyabs = Math.Abs(dy); + + // Half distance + int x = dyabs >> 1; + int y = dxabs >> 1; + + // Direction + int sdx = Math.Sign(dx); + int sdy = Math.Sign(dy); + + // Start position + int px = x1; + int py = y1; + + // When the line is completely inside screen, + // then do an unchecked draw, because all of its pixels are + // guaranteed to be within the memory range + if ((x1 >= 0) && (x2 >= 0) && (x1 < visiblewidth) && (x2 < visiblewidth) && + (y1 >= 0) && (y2 >= 0) && (y1 < visibleheight) && (y2 < visibleheight)) + { + // Draw first pixel + pixels[py * width + px] = c; + + // Check if the line is more horizontal than vertical + if (dxabs >= dyabs) + { + for (i = 0; i < dxabs; i++) + { + y += dyabs; + if (y >= dxabs) + { + y -= dxabs; + py += sdy; + } + px += sdx; + + // Draw pixel + pixels[py * width + px] = c; + } + } + // Else the line is more vertical than horizontal + else + { + for (i = 0; i < dyabs; i++) + { + x += dxabs; + if (x >= dyabs) + { + x -= dyabs; + px += sdx; + } + py += sdy; + + // Draw pixel + pixels[py * width + px] = c; + } + } + } + else + { + // Draw first pixel + if ((px >= 0) && (px < visiblewidth) && (py >= 0) && (py < visibleheight)) + pixels[py * width + px] = c; + + // Check if the line is more horizontal than vertical + if (dxabs >= dyabs) + { + for (i = 0; i < dxabs; i++) + { + y += dyabs; + if (y >= dxabs) + { + y -= dxabs; + py += sdy; + } + px += sdx; + + // Draw pixel + if ((px >= 0) && (px < visiblewidth) && (py >= 0) && (py < visibleheight)) + pixels[py * width + px] = c; + } + } + // Else the line is more vertical than horizontal + else + { + for (i = 0; i < dyabs; i++) + { + x += dxabs; + if (x >= dyabs) + { + x -= dyabs; + px += sdx; + } + py += sdy; + + // Draw pixel + if ((px >= 0) && (px < visiblewidth) && (py >= 0) && (py < visibleheight)) + pixels[py * width + px] = c; + } + } + } + } + + #endregion + } +} diff --git a/Source/Core/Rendering/Presentation.cs b/Source/Core/Rendering/Presentation.cs new file mode 100644 index 0000000..c738af0 --- /dev/null +++ b/Source/Core/Rendering/Presentation.cs @@ -0,0 +1,160 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; + +#endregion + +namespace CodeImp.DoomBuilder.Rendering +{ + /// + /// Presentation settings + /// + public class Presentation + { + // Constants for static instances + public const float THINGS_BACK_ALPHA = 0.3f; + public const float THINGS_HIDDEN_ALPHA = 0.66f; + + // Static instances + private static Presentation standard; + private static Presentation things; + + // Static properties + public static Presentation Standard { get { return standard; } } + public static Presentation Things { get { return things; } } + + // Variables + protected internal List layers; + + // Constructor + public Presentation() + { + // Initialize + layers = new List(); + } + + // Copy constructor + public Presentation(Presentation p) + { + layers = new List(p.layers); + } + + // This creates the static instances + internal static void Initialize() + { + // Standard classic mode + standard = new Presentation(); + standard.layers.Add(new PresentLayer(RendererLayer.Background, BlendingMode.Mask, General.Settings.BackgroundAlpha)); + standard.layers.Add(new PresentLayer(RendererLayer.Surface, BlendingMode.Mask)); + standard.layers.Add(new PresentLayer(RendererLayer.Things, BlendingMode.Alpha, THINGS_BACK_ALPHA)); + standard.layers.Add(new PresentLayer(RendererLayer.Grid, BlendingMode.Mask)); + standard.layers.Add(new PresentLayer(RendererLayer.Geometry, BlendingMode.Alpha, 1f, true)); + standard.layers.Add(new PresentLayer(RendererLayer.Overlay, BlendingMode.Alpha, 1f, true)); + + // Things classic mode + things = new Presentation(); + things.layers.Add(new PresentLayer(RendererLayer.Background, BlendingMode.Mask, General.Settings.BackgroundAlpha)); + things.layers.Add(new PresentLayer(RendererLayer.Surface, BlendingMode.Mask)); + things.layers.Add(new PresentLayer(RendererLayer.Grid, BlendingMode.Mask)); + things.layers.Add(new PresentLayer(RendererLayer.Geometry, BlendingMode.Alpha, 1f, true)); + things.layers.Add(new PresentLayer(RendererLayer.Things, BlendingMode.Alpha, 1f, false)); + things.layers.Add(new PresentLayer(RendererLayer.Overlay, BlendingMode.Alpha, 1f, true)); + } + } + + /// + /// Customizable Presentation + /// + public class CustomPresentation : Presentation + { + // Allow public adding + public void AddLayer(PresentLayer layer) + { + this.layers.Add(layer); + } + } + + public struct PresentLayer + { + public RendererLayer layer; + public BlendingMode blending; + public float alpha; + public bool antialiasing; + + // Constructor + public PresentLayer(RendererLayer layer, BlendingMode blending, float alpha, bool antialiasing) + { + this.layer = layer; + this.blending = blending; + this.alpha = alpha; + this.antialiasing = antialiasing; + } + + // Constructor + public PresentLayer(RendererLayer layer, BlendingMode blending, float alpha) + { + this.layer = layer; + this.blending = blending; + this.alpha = alpha; + this.antialiasing = false; + } + + // Constructor + public PresentLayer(RendererLayer layer, BlendingMode blending) + { + this.layer = layer; + this.blending = blending; + this.alpha = 1f; + this.antialiasing = false; + } + + // Constructor + public PresentLayer(RendererLayer layer) + { + this.layer = layer; + this.blending = BlendingMode.None; + this.alpha = 1f; + this.antialiasing = false; + } + } + + // The different layers + public enum RendererLayer : int + { + Background, + Grid, + Things, + Geometry, + Overlay, + Surface + } + + // Blending modes + public enum BlendingMode : int + { + None, + Mask, + Alpha, + Additive, + } +} diff --git a/Source/Core/Rendering/RenderLayers.cs b/Source/Core/Rendering/RenderLayers.cs new file mode 100644 index 0000000..5c596c0 --- /dev/null +++ b/Source/Core/Rendering/RenderLayers.cs @@ -0,0 +1,22 @@ +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; + +#endregion + +namespace CodeImp.DoomBuilder.Rendering +{ + internal enum RenderLayers : int + { + None = 0, + Background = 1, + Plotter = 2, + Things = 3, + Overlay = 4, + Surface = 5 + } +} diff --git a/Source/Core/Rendering/RenderPasses.cs b/Source/Core/Rendering/RenderPasses.cs new file mode 100644 index 0000000..56035f0 --- /dev/null +++ b/Source/Core/Rendering/RenderPasses.cs @@ -0,0 +1,48 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using System.Drawing; +using System.ComponentModel; +using SlimDX; +using CodeImp.DoomBuilder.Geometry; +using SlimDX.Direct3D9; +using CodeImp.DoomBuilder.Data; +using CodeImp.DoomBuilder.VisualModes; +using CodeImp.DoomBuilder.Map; + +#endregion + +namespace CodeImp.DoomBuilder.Rendering +{ + public enum RenderPass : int + { + Solid = 0, + Mask = 1, + Alpha = 2, + Additive = 3 + } +} + diff --git a/Source/Core/Rendering/Renderer.cs b/Source/Core/Rendering/Renderer.cs new file mode 100644 index 0000000..d13c6c8 --- /dev/null +++ b/Source/Core/Rendering/Renderer.cs @@ -0,0 +1,113 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; + +#endregion + +namespace CodeImp.DoomBuilder.Rendering +{ + internal abstract class Renderer : ID3DResource + { + #region ================== Constants + + #endregion + + #region ================== Variables + + // Graphics + protected D3DDevice graphics; + protected static bool fullbrightness; + + // Disposing + protected bool isdisposed = false; + + #endregion + + #region ================== Properties + + // Disposing + public bool IsDisposed { get { return isdisposed; } } + public static bool FullBrightness { get { return fullbrightness; } set { fullbrightness = value; } } + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + internal Renderer(D3DDevice g) + { + // Initialize + this.graphics = g; + + // Register as resource + g.RegisterResource(this); + + // We have no destructor + GC.SuppressFinalize(this); + } + + // Disposer + internal virtual void Dispose() + { + // Not already disposed? + if (!isdisposed) + { + // Clean up + + // Unregister resource + graphics.UnregisterResource(this); + + // Done + graphics = null; + isdisposed = true; + } + } + + #endregion + + #region ================== Methods + + // This calculates the sector brightness level + public int CalculateBrightness(int level) + { + float flevel = level; + + // Simulat doom light levels + if ((level < 192) && General.Map.Config.DoomLightLevels) + flevel = (192.0f - (float)(192 - level) * 1.5f); + + byte blevel = (byte)General.Clamp((int)flevel, 0, 255); + PixelColor c = new PixelColor(255, blevel, blevel, blevel); + return c.ToInt(); + } + + // This is called when the graphics need to be reset + public virtual void Reset() { } + + // For DirectX resources + public virtual void UnloadResource() { } + public virtual void ReloadResource() { } + + #endregion + } +} diff --git a/Source/Core/Rendering/Renderer2D.cs b/Source/Core/Rendering/Renderer2D.cs new file mode 100644 index 0000000..a2e2712 --- /dev/null +++ b/Source/Core/Rendering/Renderer2D.cs @@ -0,0 +1,1601 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using System.Drawing; +using System.ComponentModel; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Map; +using SlimDX.Direct3D9; +using SlimDX; +using CodeImp.DoomBuilder.Geometry; +using System.Drawing.Imaging; +using CodeImp.DoomBuilder.Data; +using CodeImp.DoomBuilder.Editing; + +#endregion + +namespace CodeImp.DoomBuilder.Rendering +{ + + /* This renders a 2D presentation of the map. This is done in several + * layers which each are optimized for a different purpose. Set the + * PresentationLayer(s) to specify how to present these layers. + */ + + internal unsafe sealed class Renderer2D : Renderer, IRenderer2D + { + #region ================== Constants + + private const float FSAA_FACTOR = 0.6f; + private const float THING_ARROW_SIZE = 1.5f; + private const float THING_ARROW_SHRINK = 2f; + private const float THING_CIRCLE_SIZE = 1f; + private const float THING_CIRCLE_SHRINK = 0f; + private const int THING_BUFFER_STEP = 100; + private const float THINGS_BACK_ALPHA = 0.3f; + + private const string FONT_NAME = "Verdana"; + private const int FONT_WIDTH = 0; + private const int FONT_HEIGHT = 0; + + private const int THING_SHINY = 1; + private const int THING_SQUARE = 2; + private const int NUM_THING_TEXTURES = 4; + internal const int NUM_VIEW_MODES = 6; // villsa + + #endregion + + #region ================== Variables + + // Rendertargets + private Texture backtex; + private Texture plottertex; + private Texture thingstex; + private Texture overlaytex; + private Texture surfacetex; + + // Locking data + private DataRectangle plotlocked; + private Surface targetsurface; + + // Rendertarget sizes + private Size windowsize; + private Size structsize; + private Size thingssize; + private Size overlaysize; + private Size backsize; + + // Font + private SlimDX.Direct3D9.Font font; + + // Geometry plotter + private Plotter plotter; + + // Vertices to present the textures + private VertexBuffer screenverts; + private FlatVertex[] backimageverts; + + // Batch buffer for things rendering + private VertexBuffer thingsvertices; + private int maxthings, numthings; + + // Render settings + private bool thingsfront; + private int vertexsize; + private RenderLayers renderlayer = RenderLayers.None; + + // Surfaces + private SurfaceManager surfaces; + + // Images + private ResourceImage[] thingtexture; + + // View settings (world coordinates) + private ViewMode viewmode; + private float scale; + private float scaleinv; + private float offsetx; + private float offsety; + private float translatex; + private float translatey; + private float linenormalsize; + private float lastgridscale = -1f; + private int lastgridsize; + private float lastgridx; + private float lastgridy; + private RectangleF viewport; + private RectangleF yviewport; + + // Presentation + private Presentation present; + + #endregion + + #region ================== Properties + + public float OffsetX { get { return offsetx; } } + public float OffsetY { get { return offsety; } } + public float TranslateX { get { return translatex; } } + public float TranslateY { get { return translatey; } } + public float Scale { get { return scale; } } + public int VertexSize { get { return vertexsize; } } + public ViewMode ViewMode { get { return viewmode; } } + public SurfaceManager Surfaces { get { return surfaces; } } + public RectangleF Viewport { get { return viewport; } } + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + internal Renderer2D(D3DDevice graphics) : base(graphics) + { + // Load thing textures + thingtexture = new ResourceImage[NUM_THING_TEXTURES]; + for (int i = 0; i < NUM_THING_TEXTURES; i++) + { + thingtexture[i] = new ResourceImage("CodeImp.DoomBuilder.Resources.Thing2D_" + i.ToString(CultureInfo.InvariantCulture) + ".png"); + thingtexture[i].UseColorCorrection = false; + thingtexture[i].LoadImage(); + thingtexture[i].CreateTexture(); + } + + // Create surface manager + surfaces = new SurfaceManager(); + + // Create rendertargets + CreateRendertargets(); + + // We have no destructor + GC.SuppressFinalize(this); + } + + // Disposer + internal override void Dispose() + { + // Not already disposed? + if (!isdisposed) + { + // Destroy rendertargets + DestroyRendertargets(); + foreach (ResourceImage i in thingtexture) i.Dispose(); + + // Dispose surface manager + surfaces.Dispose(); + + // Done + base.Dispose(); + } + } + + #endregion + + #region ================== Presenting + + // This sets the presentation to use + public void SetPresentation(Presentation present) + { + this.present = new Presentation(present); + } + + // This draws the image on screen + public void Present() + { + // Start drawing + if (graphics.StartRendering(true, General.Colors.Background.ToColorValue(), graphics.BackBuffer, graphics.DepthBuffer)) + { + // Renderstates that count for this whole sequence + graphics.Device.SetRenderState(RenderState.CullMode, Cull.None); + graphics.Device.SetRenderState(RenderState.ZEnable, false); + graphics.Device.SetRenderState(RenderState.FogEnable, false); + graphics.Device.SetStreamSource(0, screenverts, 0, sizeof(FlatVertex)); + graphics.Device.SetTransform(TransformState.World, Matrix.Identity); + graphics.Shaders.Display2D.Begin(); + + // Go for all layers + foreach (PresentLayer layer in present.layers) + { + int aapass; + + // Set blending mode + switch (layer.blending) + { + case BlendingMode.None: + graphics.Device.SetRenderState(RenderState.AlphaBlendEnable, false); + graphics.Device.SetRenderState(RenderState.AlphaTestEnable, false); + graphics.Device.SetRenderState(RenderState.TextureFactor, -1); + break; + + case BlendingMode.Mask: + graphics.Device.SetRenderState(RenderState.AlphaBlendEnable, false); + graphics.Device.SetRenderState(RenderState.AlphaTestEnable, true); + graphics.Device.SetRenderState(RenderState.TextureFactor, -1); + break; + + case BlendingMode.Alpha: + graphics.Device.SetRenderState(RenderState.AlphaBlendEnable, true); + graphics.Device.SetRenderState(RenderState.AlphaTestEnable, false); + graphics.Device.SetRenderState(RenderState.SourceBlend, Blend.SourceAlpha); + graphics.Device.SetRenderState(RenderState.DestinationBlend, Blend.InverseSourceAlpha); + graphics.Device.SetRenderState(RenderState.TextureFactor, (new Color4(layer.alpha, 1f, 1f, 1f)).ToArgb()); + break; + + case BlendingMode.Additive: + graphics.Device.SetRenderState(RenderState.AlphaBlendEnable, true); + graphics.Device.SetRenderState(RenderState.AlphaTestEnable, false); + graphics.Device.SetRenderState(RenderState.SourceBlend, Blend.SourceAlpha); + graphics.Device.SetRenderState(RenderState.DestinationBlend, Blend.One); + graphics.Device.SetRenderState(RenderState.TextureFactor, (new Color4(layer.alpha, 1f, 1f, 1f)).ToArgb()); + break; + } + + // Check which pass to use + if (layer.antialiasing && General.Settings.QualityDisplay) aapass = 0; else aapass = 1; + + // Render layer + switch (layer.layer) + { + // BACKGROUND + case RendererLayer.Background: + if ((backimageverts == null) || (General.Map.Grid.Background.Texture == null)) break; + graphics.Device.SetTexture(0, General.Map.Grid.Background.Texture); + graphics.Shaders.Display2D.Texture1 = General.Map.Grid.Background.Texture; + graphics.Shaders.Display2D.SetSettings(1f / windowsize.Width, 1f / windowsize.Height, FSAA_FACTOR, layer.alpha, false); + graphics.Shaders.Display2D.BeginPass(aapass); + graphics.Device.DrawUserPrimitives(PrimitiveType.TriangleStrip, 0, 2, backimageverts); + graphics.Shaders.Display2D.EndPass(); + graphics.Device.SetStreamSource(0, screenverts, 0, sizeof(FlatVertex)); + break; + + // GRID + case RendererLayer.Grid: + graphics.Device.SetTexture(0, backtex); + graphics.Shaders.Display2D.Texture1 = backtex; + graphics.Shaders.Display2D.SetSettings(1f / backsize.Width, 1f / backsize.Height, FSAA_FACTOR, layer.alpha, false); + graphics.Shaders.Display2D.BeginPass(aapass); + graphics.Device.DrawPrimitives(PrimitiveType.TriangleStrip, 0, 2); + graphics.Shaders.Display2D.EndPass(); + break; + + // GEOMETRY + case RendererLayer.Geometry: + graphics.Device.SetTexture(0, plottertex); + graphics.Shaders.Display2D.Texture1 = plottertex; + graphics.Shaders.Display2D.SetSettings(1f / structsize.Width, 1f / structsize.Height, FSAA_FACTOR, layer.alpha, false); + graphics.Shaders.Display2D.BeginPass(aapass); + graphics.Device.DrawPrimitives(PrimitiveType.TriangleStrip, 0, 2); + graphics.Shaders.Display2D.EndPass(); + break; + + // THINGS + case RendererLayer.Things: + graphics.Device.SetTexture(0, thingstex); + graphics.Shaders.Display2D.Texture1 = thingstex; + graphics.Shaders.Display2D.SetSettings(1f / thingssize.Width, 1f / thingssize.Height, FSAA_FACTOR, layer.alpha, false); + graphics.Shaders.Display2D.BeginPass(aapass); + graphics.Device.DrawPrimitives(PrimitiveType.TriangleStrip, 0, 2); + graphics.Shaders.Display2D.EndPass(); + break; + + // OVERLAY + case RendererLayer.Overlay: + graphics.Device.SetTexture(0, overlaytex); + graphics.Shaders.Display2D.Texture1 = overlaytex; + graphics.Shaders.Display2D.SetSettings(1f / overlaysize.Width, 1f / overlaysize.Height, FSAA_FACTOR, layer.alpha, false); + graphics.Shaders.Display2D.BeginPass(aapass); + graphics.Device.DrawPrimitives(PrimitiveType.TriangleStrip, 0, 2); + graphics.Shaders.Display2D.EndPass(); + break; + + // SURFACE + case RendererLayer.Surface: + graphics.Device.SetTexture(0, surfacetex); + graphics.Shaders.Display2D.Texture1 = surfacetex; + graphics.Shaders.Display2D.SetSettings(1f / overlaysize.Width, 1f / overlaysize.Height, FSAA_FACTOR, layer.alpha, false); + graphics.Shaders.Display2D.BeginPass(aapass); + graphics.Device.DrawPrimitives(PrimitiveType.TriangleStrip, 0, 2); + graphics.Shaders.Display2D.EndPass(); + break; + } + } + + // Done + graphics.Shaders.Display2D.End(); + graphics.FinishRendering(); + graphics.Present(); + + // Release binds + graphics.Device.SetTexture(0, null); + graphics.Shaders.Display2D.Texture1 = null; + graphics.Device.SetStreamSource(0, null, 0, 0); + } + else + { + // Request delayed redraw + General.MainWindow.DelayedRedraw(); + } + } + + #endregion + + #region ================== Management + + // This is called before a device is reset + // (when resized or display adapter was changed) + public override void UnloadResource() + { + // Destroy rendertargets + DestroyRendertargets(); + } + + // This is called resets when the device is reset + // (when resized or display adapter was changed) + public override void ReloadResource() + { + // Re-create rendertargets + CreateRendertargets(); + } + + // This resets the graphics + public override void Reset() + { + UnloadResource(); + ReloadResource(); + } + + // This destroys the rendertargets + public void DestroyRendertargets() + { + // Trash rendertargets + if (plottertex != null) plottertex.Dispose(); + if (thingstex != null) thingstex.Dispose(); + if (overlaytex != null) overlaytex.Dispose(); + if (surfacetex != null) surfacetex.Dispose(); + if (backtex != null) backtex.Dispose(); + if (screenverts != null) screenverts.Dispose(); + plottertex = null; + thingstex = null; + backtex = null; + screenverts = null; + overlaytex = null; + surfacetex = null; + + // Trash things batch buffer + if (thingsvertices != null) thingsvertices.Dispose(); + thingsvertices = null; + numthings = 0; + maxthings = 0; + lastgridscale = -1f; + lastgridsize = 0; + + // Trash font + if (font != null) font.Dispose(); + font = null; + } + + // Allocates new image memory to render on + public void CreateRendertargets() + { + SurfaceDescription sd; + DataStream stream; + FlatVertex[] verts; + + // Destroy rendertargets + DestroyRendertargets(); + + // Get new width and height + windowsize.Width = graphics.RenderTarget.ClientSize.Width; + windowsize.Height = graphics.RenderTarget.ClientSize.Height; + + // Create rendertargets textures + plottertex = new Texture(graphics.Device, windowsize.Width, windowsize.Height, 1, Usage.None, Format.A8R8G8B8, Pool.Managed); + thingstex = new Texture(graphics.Device, windowsize.Width, windowsize.Height, 1, Usage.RenderTarget, Format.A8R8G8B8, Pool.Default); + backtex = new Texture(graphics.Device, windowsize.Width, windowsize.Height, 1, Usage.None, Format.A8R8G8B8, Pool.Managed); + overlaytex = new Texture(graphics.Device, windowsize.Width, windowsize.Height, 1, Usage.RenderTarget, Format.A8R8G8B8, Pool.Default); + surfacetex = new Texture(graphics.Device, windowsize.Width, windowsize.Height, 1, Usage.RenderTarget, Format.A8R8G8B8, Pool.Default); + + // Get the real surface sizes + sd = plottertex.GetLevelDescription(0); + structsize.Width = sd.Width; + structsize.Height = sd.Height; + sd = thingstex.GetLevelDescription(0); + thingssize.Width = sd.Width; + thingssize.Height = sd.Height; + sd = backtex.GetLevelDescription(0); + backsize.Width = sd.Width; + backsize.Height = sd.Height; + sd = overlaytex.GetLevelDescription(0); + overlaysize.Width = sd.Width; + overlaysize.Height = sd.Height; + + // Clear rendertargets + // This may cause a crash when resetting because it recursively + // calls Reset in the Start functions and doesn't get to Finish + //StartPlotter(true); Finish(); + //StartThings(true); Finish(); + //StartOverlay(true); Finish(); + graphics.ClearRendertarget(General.Colors.Background.WithAlpha(0).ToColorValue(), thingstex.GetSurfaceLevel(0), null); + graphics.ClearRendertarget(General.Colors.Background.WithAlpha(0).ToColorValue(), overlaytex.GetSurfaceLevel(0), null); + + // Create font + font = new SlimDX.Direct3D9.Font(graphics.Device, FONT_WIDTH, FONT_HEIGHT, FontWeight.Bold, 1, false, CharacterSet.Ansi, Precision.Default, FontQuality.Antialiased, PitchAndFamily.Default, FONT_NAME); + + // Create vertex buffers + screenverts = new VertexBuffer(graphics.Device, 4 * sizeof(FlatVertex), Usage.Dynamic | Usage.WriteOnly, VertexFormat.None, Pool.Default); + + // Make screen vertices + stream = screenverts.Lock(0, 4 * sizeof(FlatVertex), LockFlags.Discard | LockFlags.NoSystemLock); + verts = CreateScreenVerts(structsize); + stream.WriteRange(verts); + screenverts.Unlock(); + stream.Dispose(); + + // Force update of view + lastgridscale = -1f; + lastgridsize = 0; + lastgridx = 0.0f; + lastgridy = 0.0f; + UpdateTransformations(); + } + + // This makes screen vertices for display + private FlatVertex[] CreateScreenVerts(Size texturesize) + { + FlatVertex[] screenverts = new FlatVertex[4]; + screenverts[0].x = 0.5f; + screenverts[0].y = 0.5f; + screenverts[0].c = -1; + screenverts[0].u = 1f / texturesize.Width; + screenverts[0].v = 1f / texturesize.Height; + screenverts[1].x = texturesize.Width - 1.5f; + screenverts[1].y = 0.5f; + screenverts[1].c = -1; + screenverts[1].u = 1f - 1f / texturesize.Width; + screenverts[1].v = 1f / texturesize.Height; + screenverts[2].x = 0.5f; + screenverts[2].y = texturesize.Height - 1.5f; + screenverts[2].c = -1; + screenverts[2].u = 1f / texturesize.Width; + screenverts[2].v = 1f - 1f / texturesize.Height; + screenverts[3].x = texturesize.Width - 1.5f; + screenverts[3].y = texturesize.Height - 1.5f; + screenverts[3].c = -1; + screenverts[3].u = 1f - 1f / texturesize.Width; + screenverts[3].v = 1f - 1f / texturesize.Height; + return screenverts; + } + + #endregion + + #region ================== View + + // This changes view mode + public void SetViewMode(ViewMode mode) + { + viewmode = mode; + } + + // This changes view position + public void PositionView(float x, float y) + { + // Change position in world coordinates + offsetx = x; + offsety = y; + UpdateTransformations(); + } + + // This changes zoom + public void ScaleView(float scale) + { + // Change zoom scale + this.scale = scale; + UpdateTransformations(); + + // Show zoom on main window + General.MainWindow.UpdateZoom(scale); + } + + // This updates some maths + private void UpdateTransformations() + { + scaleinv = 1f / scale; + translatex = -offsetx + (windowsize.Width * 0.5f) * scaleinv; + translatey = -offsety - (windowsize.Height * 0.5f) * scaleinv; + linenormalsize = 10f * scaleinv; + vertexsize = (int)(1.7f * scale + 0.5f); + if (vertexsize < 0) vertexsize = 0; + if (vertexsize > 4) vertexsize = 4; + Matrix scaling = Matrix.Scaling((1f / (float)windowsize.Width) * 2f, (1f / (float)windowsize.Height) * -2f, 1f); + Matrix translate = Matrix.Translation(-(float)windowsize.Width * 0.5f, -(float)windowsize.Height * 0.5f, 0f); + graphics.Device.SetTransform(TransformState.View, Matrix.Multiply(translate, scaling)); + graphics.Device.SetTransform(TransformState.Projection, Matrix.Identity); + Vector2D lt = DisplayToMap(new Vector2D(0.0f, 0.0f)); + Vector2D rb = DisplayToMap(new Vector2D((float)windowsize.Width, (float)windowsize.Height)); + viewport = new RectangleF(lt.x, lt.y, rb.x - lt.x, rb.y - lt.y); + yviewport = new RectangleF(lt.x, rb.y, rb.x - lt.x, lt.y - rb.y); + } + + // This sets the world matrix for transformation + private void SetWorldTransformation(bool transform) + { + if (transform) + { + Matrix translate = Matrix.Translation(translatex, translatey, 0f); + Matrix scaling = Matrix.Scaling(scale, -scale, 1f); + graphics.Device.SetTransform(TransformState.World, Matrix.Multiply(translate, scaling)); + } + else + { + graphics.Device.SetTransform(TransformState.World, Matrix.Identity); + } + } + + /// + /// This unprojects display coordinates (screen space) to map coordinates + /// + public Vector2D DisplayToMap(Vector2D mousepos) + { + return mousepos.GetInvTransformed(-translatex, -translatey, scaleinv, -scaleinv); + } + + /// + /// This projects map coordinates to display coordinates (screen space) + /// + public Vector2D MapToDisplay(Vector2D mappos) + { + return mappos.GetTransformed(translatex, translatey, scale, -scale); + } + + #endregion + + #region ================== Colors + + // This returns the color for a thing + public PixelColor DetermineThingColor(Thing t) + { + // Determine color + if (t.Selected) return General.Colors.Selection; + else return t.Color; + } + + // This returns the color for a vertex + public int DetermineVertexColor(Vertex v) + { + // Determine color + if (v.Selected) return ColorCollection.SELECTION; + else return ColorCollection.VERTICES; + } + + // This returns the color for a linedef + public PixelColor DetermineLinedefColor(Linedef l) + { + if (l.Selected) + return General.Colors.Selection; + else if (l.TagonlyFlag) + return General.Colors.Tagonly.WithAlpha(General.Settings.DoubleSidedAlphaByte); // villsa + else if (l.ImpassableFlag) + { + // Impassable lines + if (l.Action != 0) return General.Colors.Actions; + else return General.Colors.Linedefs; + } + else + { + // Passable lines + if (l.Action != 0) return General.Colors.Actions.WithAlpha(General.Settings.DoubleSidedAlphaByte); + else if (l.TagonlyFlag) return General.Colors.Tagonly.WithAlpha(General.Settings.DoubleSidedAlphaByte); // villsa + else if (l.BlockSoundFlag) return General.Colors.Sounds.WithAlpha(General.Settings.DoubleSidedAlphaByte); + else if (l.MonsterBlockFlag) return General.Colors.MBlock.WithAlpha(General.Settings.DoubleSidedAlphaByte); + else if (l.SecretFlag) return General.Colors.Secret.WithAlpha(General.Settings.DoubleSidedAlphaByte); + else if (l.InvisibleFlag) return General.Colors.Invisible.WithAlpha(General.Settings.DoubleSidedAlphaByte); // villsa + else return General.Colors.Linedefs.WithAlpha(General.Settings.DoubleSidedAlphaByte); + } + } + + #endregion + + #region ================== Start / Finish + + // This begins a drawing session + public unsafe bool StartPlotter(bool clear) + { + if (renderlayer != RenderLayers.None) throw new InvalidOperationException("Renderer starting called before finished previous layer. Call Finish() first!"); + renderlayer = RenderLayers.Plotter; + try { graphics.Device.SetRenderState(RenderState.FogEnable, false); } catch (Exception) { } + + // Rendertargets available? + if (plottertex != null) + { + // Lock structures rendertarget memory + plotlocked = plottertex.LockRectangle(0, LockFlags.NoSystemLock); + + // Create structures plotter + plotter = new Plotter((PixelColor*)plotlocked.Data.DataPointer.ToPointer(), plotlocked.Pitch / sizeof(PixelColor), structsize.Height, structsize.Width, structsize.Height); + if (clear) plotter.Clear(); + + // Redraw grid when structures image was cleared + if (clear) + { + RenderBackgroundGrid(); + SetupBackground(); + } + + // Ready for rendering + UpdateTransformations(); + return true; + } + else + { + // Can't render! + Finish(); + return false; + } + } + + // This begins a drawing session + public unsafe bool StartThings(bool clear) + { + if (renderlayer != RenderLayers.None) throw new InvalidOperationException("Renderer starting called before finished previous layer. Call Finish() first!"); + renderlayer = RenderLayers.Things; + try { graphics.Device.SetRenderState(RenderState.FogEnable, false); } catch (Exception) { } + + // Rendertargets available? + if (thingstex != null) + { + // Always trash things batch buffer + if (thingsvertices != null) thingsvertices.Dispose(); + thingsvertices = null; + numthings = 0; + maxthings = 0; + + // Set the rendertarget to the things texture + targetsurface = thingstex.GetSurfaceLevel(0); + if (graphics.StartRendering(clear, General.Colors.Background.WithAlpha(0).ToColorValue(), targetsurface, null)) + { + // Ready for rendering + UpdateTransformations(); + return true; + } + else + { + // Can't render! + Finish(); + return false; + } + } + else + { + // Can't render! + Finish(); + return false; + } + } + + // This begins a drawing session + public unsafe bool StartOverlay(bool clear) + { + if (renderlayer != RenderLayers.None) throw new InvalidOperationException("Renderer starting called before finished previous layer. Call Finish() first!"); + renderlayer = RenderLayers.Overlay; + try { graphics.Device.SetRenderState(RenderState.FogEnable, false); } catch (Exception) { } + + // Rendertargets available? + if (overlaytex != null) + { + // Set the rendertarget to the things texture + targetsurface = overlaytex.GetSurfaceLevel(0); + if (graphics.StartRendering(clear, General.Colors.Background.WithAlpha(0).ToColorValue(), targetsurface, null)) + { + // Ready for rendering + UpdateTransformations(); + return true; + } + else + { + // Can't render! + Finish(); + return false; + } + } + else + { + // Can't render! + Finish(); + return false; + } + } + + // This ends a drawing session + public void Finish() + { + // Clean up plotter + if (renderlayer == RenderLayers.Plotter) + { + if (plottertex != null) plottertex.UnlockRectangle(0); + if (plotlocked.Data != null) plotlocked.Data.Dispose(); + plotter = null; + } + + // Clean up things / overlay + if ((renderlayer == RenderLayers.Things) || (renderlayer == RenderLayers.Overlay) || (renderlayer == RenderLayers.Surface)) + { + // Stop rendering + graphics.FinishRendering(); + + // Release rendertarget + try + { + graphics.Device.DepthStencilSurface = graphics.DepthBuffer; + graphics.Device.SetRenderTarget(0, graphics.BackBuffer); + } + catch (Exception) { } + if (targetsurface != null) targetsurface.Dispose(); + targetsurface = null; + } + + // Done + renderlayer = RenderLayers.None; + } + + #endregion + + #region ================== Background + + // This sets up background image vertices + private void SetupBackground() + { + Vector2D ltpos, rbpos; + Vector2D backoffset = new Vector2D((float)General.Map.Grid.BackgroundX, (float)General.Map.Grid.BackgroundY); + Vector2D backimagesize = new Vector2D((float)General.Map.Grid.Background.ScaledWidth, (float)General.Map.Grid.Background.ScaledHeight); + Vector2D backimagescale = new Vector2D(General.Map.Grid.BackgroundScaleX, General.Map.Grid.BackgroundScaleY); + + // Scale the background image size + backimagesize *= backimagescale; + + // Only if a background image is set + if ((General.Map.Grid.Background != null) && + !(General.Map.Grid.Background is UnknownImage)) + { + // Make vertices + backimageverts = CreateScreenVerts(windowsize); + + // Determine map coordinates for view window + ltpos = DisplayToMap(new Vector2D(0f, 0f)); + rbpos = DisplayToMap(new Vector2D(windowsize.Width, windowsize.Height)); + + // Offset by given background offset + ltpos -= backoffset; + rbpos -= backoffset; + + // Calculate UV coordinates + // NOTE: backimagesize.y is made negative to match Doom's coordinate system + backimageverts[0].u = ltpos.x / backimagesize.x; + backimageverts[0].v = ltpos.y / -backimagesize.y; + backimageverts[1].u = rbpos.x / backimagesize.x; + backimageverts[1].v = ltpos.y / -backimagesize.y; + backimageverts[2].u = ltpos.x / backimagesize.x; + backimageverts[2].v = rbpos.y / -backimagesize.y; + backimageverts[3].u = rbpos.x / backimagesize.x; + backimageverts[3].v = rbpos.y / -backimagesize.y; + } + else + { + // No background image + backimageverts = null; + } + } + + // This renders all grid + private void RenderBackgroundGrid() + { + Plotter gridplotter; + DataRectangle lockedrect; + + // Do we need to redraw grid? + if ((lastgridsize != General.Map.Grid.GridSize) || (lastgridscale != scale) || + (lastgridx != offsetx) || (lastgridy != offsety)) + { + // Lock background rendertarget memory + lockedrect = backtex.LockRectangle(0, LockFlags.NoSystemLock); + + // Create a plotter + gridplotter = new Plotter((PixelColor*)lockedrect.Data.DataPointer.ToPointer(), lockedrect.Pitch / sizeof(PixelColor), backsize.Height, backsize.Width, backsize.Height); + gridplotter.Clear(); + + // Render normal grid + RenderGrid(General.Map.Grid.GridSize, General.Colors.Grid, gridplotter); + + // Render 64 grid + if (General.Map.Grid.GridSize <= 64) RenderGrid(64f, General.Colors.Grid64, gridplotter); + + // Done + backtex.UnlockRectangle(0); + lockedrect.Data.Dispose(); + lastgridscale = scale; + lastgridsize = General.Map.Grid.GridSize; + lastgridx = offsetx; + lastgridy = offsety; + } + } + + // This renders the grid + private void RenderGrid(float size, PixelColor c, Plotter gridplotter) + { + Vector2D ltpos, rbpos; + Vector2D tlb, rbb; + Vector2D pos = new Vector2D(); + float sizeinv = 1f / size; + float ystart, yend; + float xstart, xend; + float from, to; + + // Only render grid when not screen-filling + if ((size * scale) > 6f) + { + // Determine map coordinates for view window + ltpos = DisplayToMap(new Vector2D(0, 0)); + rbpos = DisplayToMap(new Vector2D(windowsize.Width, windowsize.Height)); + + // Clip to nearest grid + ltpos = GridSetup.SnappedToGrid(ltpos, size, sizeinv); + rbpos = GridSetup.SnappedToGrid(rbpos, size, sizeinv); + + // Translate top left boundary and right bottom boundary of map + // to screen coords + tlb = new Vector2D(General.Map.Config.LeftBoundary, General.Map.Config.TopBoundary).GetTransformed(translatex, translatey, scale, -scale); + rbb = new Vector2D(General.Map.Config.RightBoundary, General.Map.Config.BottomBoundary).GetTransformed(translatex, translatey, scale, -scale); + + // Draw all horizontal grid lines + ystart = rbpos.y > General.Map.Config.BottomBoundary ? rbpos.y : General.Map.Config.BottomBoundary; + yend = ltpos.y < General.Map.Config.TopBoundary ? ltpos.y : General.Map.Config.TopBoundary; + + for (float y = ystart; y < yend + size; y += size) + { + if (y > General.Map.Config.TopBoundary) y = General.Map.Config.TopBoundary; + else if (y < General.Map.Config.BottomBoundary) y = General.Map.Config.BottomBoundary; + + from = tlb.x < 0 ? 0 : tlb.x; + to = rbb.x > windowsize.Width ? windowsize.Width : rbb.x; + + pos.y = y; + pos = pos.GetTransformed(translatex, translatey, scale, -scale); + + // Note: I'm not using Math.Ceiling in this case, because that doesn't work right. + gridplotter.DrawGridLineH((int)pos.y, (int)Math.Round(from + 0.49999f), (int)Math.Round(to + 0.49999f), ref c); + } + + // Draw all vertical grid lines + xstart = ltpos.x > General.Map.Config.LeftBoundary ? ltpos.x : General.Map.Config.LeftBoundary; + xend = rbpos.x < General.Map.Config.RightBoundary ? rbpos.x : General.Map.Config.RightBoundary; + + for (float x = xstart; x < xend + size; x += size) + { + if (x > General.Map.Config.RightBoundary) x = General.Map.Config.RightBoundary; + else if (x < General.Map.Config.LeftBoundary) x = General.Map.Config.LeftBoundary; + + from = tlb.y < 0 ? 0 : tlb.y; + to = rbb.y > windowsize.Height ? windowsize.Height : rbb.y; + + pos.x = x; + pos = pos.GetTransformed(translatex, translatey, scale, -scale); + + // Note: I'm not using Math.Ceiling in this case, because that doesn't work right. + gridplotter.DrawGridLineV((int)pos.x, (int)Math.Round(from + 0.49999f), (int)Math.Round(to + 0.49999f), ref c); + } + } + } + + #endregion + + #region ================== Things + + // This ensures there is enough place in the things buffer + private void ReserveThingsMemory(int newnumthings, bool preserve) + { + int newmaxthings; + DataStream stream; + FlatVertex[] verts = null; + + // Do we need to make changes? + if ((newnumthings > maxthings) || !preserve) + { + // Read old things data if we want to keep it + if (preserve && (thingsvertices != null) && (numthings > 0)) + { + stream = thingsvertices.Lock(0, numthings * 12 * FlatVertex.Stride, LockFlags.ReadOnly | LockFlags.NoSystemLock); + verts = stream.ReadRange(numthings * 12); + thingsvertices.Unlock(); + stream.Dispose(); + } + + // Buffer needs to be reallocated? + if (newnumthings > maxthings) + { + // Calculate new size + newmaxthings = newnumthings + THING_BUFFER_STEP; + + // Trash old buffer + if (thingsvertices != null) thingsvertices.Dispose(); + + // Create new buffer + thingsvertices = new VertexBuffer(graphics.Device, newmaxthings * 12 * FlatVertex.Stride, Usage.None, VertexFormat.None, Pool.Managed); + maxthings = newmaxthings; + } + else + { + // Buffer stays the same + newmaxthings = maxthings; + } + + // Keep old things? + if (preserve && (verts != null)) + { + // Write old things into new buffer + stream = thingsvertices.Lock(0, maxthings * 12 * FlatVertex.Stride, LockFlags.Discard | LockFlags.NoSystemLock); + stream.WriteRange(verts); + thingsvertices.Unlock(); + stream.Dispose(); + } + else + { + // Things were trashed + numthings = 0; + } + } + } + + // This makes vertices for a thing + // Returns false when not on the screen + private bool CreateThingVerts(Thing t, ref FlatVertex[] verts, int offset, PixelColor c) + { + float circlesize; + float arrowsize; + int color; + + // Transform to screen coordinates + Vector2D screenpos = ((Vector2D)t.Position).GetTransformed(translatex, translatey, scale, -scale); + + // Determine sizes + if (t.FixedSize && (scale > 1.0f)) + { + circlesize = (t.Size - THING_CIRCLE_SHRINK) * THING_CIRCLE_SIZE; + arrowsize = (t.Size - THING_ARROW_SHRINK) * THING_ARROW_SIZE; + } + else + { + circlesize = (t.Size - THING_CIRCLE_SHRINK) * scale * THING_CIRCLE_SIZE; + arrowsize = (t.Size - THING_ARROW_SHRINK) * scale * THING_ARROW_SIZE; + } + + // Check if the thing is actually on screen + if (((screenpos.x + circlesize) > 0.0f) && ((screenpos.x - circlesize) < (float)windowsize.Width) && + ((screenpos.y + circlesize) > 0.0f) && ((screenpos.y - circlesize) < (float)windowsize.Height)) + { + // Get integral color + color = c.ToInt(); + + // Setup fixed rect for circle + verts[offset].x = screenpos.x - circlesize; + verts[offset].y = screenpos.y - circlesize; + verts[offset].c = color; + verts[offset].u = 1f / 512f; + verts[offset].v = 1f / 128f; + offset++; + verts[offset].x = screenpos.x + circlesize; + verts[offset].y = screenpos.y - circlesize; + verts[offset].c = color; + verts[offset].u = 0.25f - 1f / 512f; + verts[offset].v = 1f / 128f; + offset++; + verts[offset].x = screenpos.x - circlesize; + verts[offset].y = screenpos.y + circlesize; + verts[offset].c = color; + verts[offset].u = 1f / 512f; + verts[offset].v = 1f - 1f / 128f; + offset++; + verts[offset] = verts[offset - 2]; + offset++; + verts[offset] = verts[offset - 2]; + offset++; + verts[offset].x = screenpos.x + circlesize; + verts[offset].y = screenpos.y + circlesize; + verts[offset].c = color; + verts[offset].u = 0.25f - 1f / 512f; + verts[offset].v = 1f - 1f / 128f; + offset++; + + // Setup rotated rect for arrow + verts[offset].x = screenpos.x + (float)Math.Sin(t.Angle - Angle2D.PI * 0.25f) * arrowsize; + verts[offset].y = screenpos.y + (float)Math.Cos(t.Angle - Angle2D.PI * 0.25f) * arrowsize; + verts[offset].c = -1; + verts[offset].u = 0.50f + t.IconOffset; + verts[offset].v = 0f; + offset++; + verts[offset].x = screenpos.x + (float)Math.Sin(t.Angle + Angle2D.PI * 0.25f) * arrowsize; + verts[offset].y = screenpos.y + (float)Math.Cos(t.Angle + Angle2D.PI * 0.25f) * arrowsize; + verts[offset].c = -1; + verts[offset].u = 0.75f + t.IconOffset; + verts[offset].v = 0f; + offset++; + verts[offset].x = screenpos.x + (float)Math.Sin(t.Angle - Angle2D.PI * 0.75f) * arrowsize; + verts[offset].y = screenpos.y + (float)Math.Cos(t.Angle - Angle2D.PI * 0.75f) * arrowsize; + verts[offset].c = -1; + verts[offset].u = 0.50f + t.IconOffset; + verts[offset].v = 1f; + offset++; + verts[offset] = verts[offset - 2]; + offset++; + verts[offset] = verts[offset - 2]; + offset++; + verts[offset].x = screenpos.x + (float)Math.Sin(t.Angle + Angle2D.PI * 0.75f) * arrowsize; + verts[offset].y = screenpos.y + (float)Math.Cos(t.Angle + Angle2D.PI * 0.75f) * arrowsize; + verts[offset].c = -1; + verts[offset].u = 0.75f + t.IconOffset; + verts[offset].v = 1f; + + // Done + return true; + } + else + { + // Not on screen + return false; + } + } + + // This draws a set of things + private void RenderThingsBatch(int offset, int count, float alpha) + { + int thingtextureindex = 0; + + // Anything to render? + if (count > 0) + { + // Make alpha color + Color4 alphacolor = new Color4(alpha, 1.0f, 1.0f, 1.0f); + + // Set renderstates for things rendering + graphics.Device.SetRenderState(RenderState.CullMode, Cull.None); + graphics.Device.SetRenderState(RenderState.ZEnable, false); + graphics.Device.SetRenderState(RenderState.AlphaBlendEnable, true); + graphics.Device.SetRenderState(RenderState.SourceBlend, Blend.SourceAlpha); + graphics.Device.SetRenderState(RenderState.DestinationBlend, Blend.InverseSourceAlpha); + graphics.Device.SetRenderState(RenderState.AlphaTestEnable, false); + graphics.Device.SetRenderState(RenderState.FogEnable, false); + graphics.Device.SetRenderState(RenderState.TextureFactor, alphacolor.ToArgb()); + graphics.Device.SetStreamSource(0, thingsvertices, 0, FlatVertex.Stride); + + // Determine things texture to use + if (General.Settings.QualityDisplay) thingtextureindex |= THING_SHINY; + if (General.Settings.SquareThings) thingtextureindex |= THING_SQUARE; + graphics.Device.SetTexture(0, thingtexture[thingtextureindex].Texture); + graphics.Shaders.Things2D.Texture1 = thingtexture[thingtextureindex].Texture; + SetWorldTransformation(false); + graphics.Shaders.Things2D.SetSettings(alpha); + + // Draw the things batched + graphics.Shaders.Things2D.Begin(); + graphics.Shaders.Things2D.BeginPass(0); + graphics.Device.DrawPrimitives(PrimitiveType.TriangleList, offset * 12, count * 4); + graphics.Shaders.Things2D.EndPass(); + graphics.Shaders.Things2D.End(); + } + } + + // This adds a thing in the things buffer for rendering + public void RenderThing(Thing t, PixelColor c, float alpha) + { + FlatVertex[] verts = new FlatVertex[12]; + DataStream stream; + + // Create vertices + if (CreateThingVerts(t, ref verts, 0, c)) + { + // Make sure there is enough memory reserved + ReserveThingsMemory(numthings + 1, true); + + // Store vertices in buffer + if (thingsvertices != null) + { + stream = thingsvertices.Lock(numthings * 12 * FlatVertex.Stride, 12 * FlatVertex.Stride, LockFlags.NoSystemLock); + stream.WriteRange(verts); + thingsvertices.Unlock(); + stream.Dispose(); + } + + // Thing added, render it + RenderThingsBatch(numthings, 1, alpha); + numthings++; + } + } + + // This adds a thing in the things buffer for rendering + public void RenderThingSet(ICollection things, float alpha) + { + // Anything to do? + if (things.Count > 0) + { + FlatVertex[] verts = new FlatVertex[things.Count * 12]; + + // Make sure there is enough memory reserved + ReserveThingsMemory(numthings + things.Count, true); + + // Go for all things + int addcount = 0; + foreach (Thing t in things) + { + // Create vertices + if (CreateThingVerts(t, ref verts, addcount * 12, DetermineThingColor(t))) + { + // Next + addcount++; + } + } + + // Store vertices in buffer + if (thingsvertices != null) + { + DataStream stream = thingsvertices.Lock(numthings * 12 * FlatVertex.Stride, things.Count * 12 * FlatVertex.Stride, LockFlags.NoSystemLock); + stream.WriteRange(verts); + thingsvertices.Unlock(); + stream.Dispose(); + } + + // Things added, render them + RenderThingsBatch(numthings, addcount, alpha); + numthings += addcount; + } + } + + #endregion + + #region ================== Surface + + // This redraws the surface + public void RedrawSurface() + { + if (renderlayer != RenderLayers.None) throw new InvalidOperationException("Renderer starting called before finished previous layer. Call Finish() first!"); + renderlayer = RenderLayers.Surface; + + // Rendertargets available? + if (surfacetex != null) + { + // Set the rendertarget to the surface texture + targetsurface = surfacetex.GetSurfaceLevel(0); + if (graphics.StartRendering(true, General.Colors.Background.WithAlpha(0).ToColorValue(), targetsurface, null)) + { + // Make sure anything we need is loaded + General.Map.Data.UnknownTexture3D.CreateTexture(); + + // Set transformations + UpdateTransformations(); + + // Set states + graphics.Device.SetRenderState(RenderState.CullMode, Cull.None); + graphics.Device.SetRenderState(RenderState.ZEnable, false); + graphics.Device.SetRenderState(RenderState.AlphaBlendEnable, false); + graphics.Device.SetRenderState(RenderState.AlphaTestEnable, false); + graphics.Device.SetRenderState(RenderState.TextureFactor, -1); + graphics.Device.SetRenderState(RenderState.FogEnable, false); + SetWorldTransformation(true); + graphics.Shaders.Display2D.SetSettings(1f, 1f, 0f, 1f, General.Settings.ClassicBilinear); + + // Prepare for rendering + switch (viewmode) + { + case ViewMode.FloorColor: // villsa + case ViewMode.CeilingColor: // villsa + case ViewMode.ThingColor: // villsa + surfaces.RenderSectorBrightness(yviewport); + surfaces.RenderSectorSurfaces(graphics); + break; + + case ViewMode.FloorTextures: + surfaces.RenderSectorFloors(yviewport); + surfaces.RenderSectorSurfaces(graphics); + break; + + case ViewMode.CeilingTextures: + surfaces.RenderSectorCeilings(yviewport); + surfaces.RenderSectorSurfaces(graphics); + break; + } + } + } + + // Done + Finish(); + } + + #endregion + + #region ================== Overlay + + // This renders geometry + // The geometry must be a triangle list + public void RenderGeometry(FlatVertex[] vertices, ImageData texture, bool transformcoords) + { + Texture t = null; + + if (vertices.Length > 0) + { + if (texture != null) + { + // Make sure the texture is loaded + if (!texture.IsImageLoaded) texture.LoadImage(); + if (texture.Texture == null) texture.CreateTexture(); + t = texture.Texture; + } + else + { + t = General.Map.Data.WhiteTexture.Texture; + } + + // Set renderstates for rendering + graphics.Device.SetRenderState(RenderState.CullMode, Cull.None); + graphics.Device.SetRenderState(RenderState.ZEnable, false); + graphics.Device.SetRenderState(RenderState.AlphaBlendEnable, false); + graphics.Device.SetRenderState(RenderState.AlphaTestEnable, false); + graphics.Device.SetRenderState(RenderState.TextureFactor, -1); + graphics.Device.SetRenderState(RenderState.FogEnable, false); + graphics.Shaders.Display2D.Texture1 = t; + graphics.Device.SetTexture(0, t); + SetWorldTransformation(transformcoords); + graphics.Shaders.Display2D.SetSettings(1f, 1f, 0f, 1f, General.Settings.ClassicBilinear); + + // Draw + graphics.Shaders.Display2D.Begin(); + graphics.Shaders.Display2D.BeginPass(1); + graphics.Device.DrawUserPrimitives(PrimitiveType.TriangleList, 0, vertices.Length / 3, vertices); + graphics.Shaders.Display2D.EndPass(); + graphics.Shaders.Display2D.End(); + } + } + + // This renders text + public void RenderText(TextLabel text) + { + // Update the text if needed + text.Update(translatex, translatey, scale, -scale); + + // Text is created? + if (text.VertexBuffer != null) + { + // Set renderstates for rendering + graphics.Device.SetRenderState(RenderState.CullMode, Cull.None); + graphics.Device.SetRenderState(RenderState.ZEnable, false); + graphics.Device.SetRenderState(RenderState.AlphaBlendEnable, true); + graphics.Device.SetRenderState(RenderState.AlphaTestEnable, false); + graphics.Device.SetRenderState(RenderState.TextureFactor, -1); + graphics.Device.SetRenderState(RenderState.FogEnable, false); + graphics.Shaders.Display2D.Texture1 = graphics.FontTexture; + SetWorldTransformation(false); + graphics.Shaders.Display2D.SetSettings(1f, 1f, 0f, 1f, true); + graphics.Device.SetTexture(0, graphics.FontTexture); + graphics.Device.SetStreamSource(0, text.VertexBuffer, 0, FlatVertex.Stride); + + // Draw + graphics.Shaders.Display2D.Begin(); + graphics.Shaders.Display2D.BeginPass(2); + graphics.Device.DrawPrimitives(PrimitiveType.TriangleList, 0, text.NumFaces >> 1); + graphics.Device.DrawPrimitives(PrimitiveType.TriangleList, 0, text.NumFaces); + graphics.Shaders.Display2D.EndPass(); + graphics.Shaders.Display2D.End(); + } + } + + // This renders a rectangle with given border size and color + public void RenderRectangle(RectangleF rect, float bordersize, PixelColor c, bool transformrect) + { + FlatQuad[] quads = new FlatQuad[4]; + + /* + * Rectangle setup: + * + * -------------------------- + * |___________0____________| + * | | | | + * | | | | + * | | | | + * | 2| |3 | + * | | | | + * | | | | + * |__|__________________|__| + * | 1 | + * -------------------------- + * + * Don't you just love ASCII art? + */ + + // Calculate positions + Vector2D lt = new Vector2D(rect.Left, rect.Top); + Vector2D rb = new Vector2D(rect.Right, rect.Bottom); + if (transformrect) + { + lt = lt.GetTransformed(translatex, translatey, scale, -scale); + rb = rb.GetTransformed(translatex, translatey, scale, -scale); + } + + // Make quads + quads[0] = new FlatQuad(PrimitiveType.TriangleStrip, lt.x, lt.y, rb.x, lt.y - bordersize); + quads[1] = new FlatQuad(PrimitiveType.TriangleStrip, lt.x, rb.y + bordersize, rb.x, rb.y); + quads[2] = new FlatQuad(PrimitiveType.TriangleStrip, lt.x, lt.y - bordersize, lt.x + bordersize, rb.y + bordersize); + quads[3] = new FlatQuad(PrimitiveType.TriangleStrip, rb.x - bordersize, lt.y - bordersize, rb.x, rb.y + bordersize); + quads[0].SetColors(c.ToInt()); + quads[1].SetColors(c.ToInt()); + quads[2].SetColors(c.ToInt()); + quads[3].SetColors(c.ToInt()); + + // Set renderstates for rendering + graphics.Device.SetRenderState(RenderState.CullMode, Cull.None); + graphics.Device.SetRenderState(RenderState.ZEnable, false); + graphics.Device.SetRenderState(RenderState.AlphaBlendEnable, false); + graphics.Device.SetRenderState(RenderState.AlphaTestEnable, false); + graphics.Device.SetRenderState(RenderState.TextureFactor, -1); + graphics.Device.SetRenderState(RenderState.FogEnable, false); + SetWorldTransformation(false); + graphics.Device.SetTexture(0, General.Map.Data.WhiteTexture.Texture); + graphics.Shaders.Display2D.Texture1 = General.Map.Data.WhiteTexture.Texture; + graphics.Shaders.Display2D.SetSettings(1f, 1f, 0f, 1f, General.Settings.ClassicBilinear); + + // Draw + graphics.Shaders.Display2D.Begin(); + graphics.Shaders.Display2D.BeginPass(1); + quads[0].Render(graphics); + quads[1].Render(graphics); + quads[2].Render(graphics); + quads[3].Render(graphics); + graphics.Shaders.Display2D.EndPass(); + graphics.Shaders.Display2D.End(); + } + + // This renders a filled rectangle with given color + public void RenderRectangleFilled(RectangleF rect, PixelColor c, bool transformrect) + { + // Calculate positions + Vector2D lt = new Vector2D(rect.Left, rect.Top); + Vector2D rb = new Vector2D(rect.Right, rect.Bottom); + if (transformrect) + { + lt = lt.GetTransformed(translatex, translatey, scale, -scale); + rb = rb.GetTransformed(translatex, translatey, scale, -scale); + } + + // Make quad + FlatQuad quad = new FlatQuad(PrimitiveType.TriangleStrip, lt.x, lt.y, rb.x, rb.y); + quad.SetColors(c.ToInt()); + + // Set renderstates for rendering + graphics.Device.SetRenderState(RenderState.CullMode, Cull.None); + graphics.Device.SetRenderState(RenderState.ZEnable, false); + graphics.Device.SetRenderState(RenderState.AlphaBlendEnable, false); + graphics.Device.SetRenderState(RenderState.AlphaTestEnable, false); + graphics.Device.SetRenderState(RenderState.TextureFactor, -1); + graphics.Device.SetRenderState(RenderState.FogEnable, false); + SetWorldTransformation(false); + graphics.Device.SetTexture(0, General.Map.Data.WhiteTexture.Texture); + graphics.Shaders.Display2D.Texture1 = General.Map.Data.WhiteTexture.Texture; + graphics.Shaders.Display2D.SetSettings(1f, 1f, 0f, 1f, General.Settings.ClassicBilinear); + + // Draw + graphics.Shaders.Display2D.Begin(); + graphics.Shaders.Display2D.BeginPass(1); + quad.Render(graphics); + graphics.Shaders.Display2D.EndPass(); + graphics.Shaders.Display2D.End(); + } + + // This renders a filled rectangle with given color + public void RenderRectangleFilled(RectangleF rect, PixelColor c, bool transformrect, ImageData texture) + { + // Calculate positions + Vector2D lt = new Vector2D(rect.Left, rect.Top); + Vector2D rb = new Vector2D(rect.Right, rect.Bottom); + if (transformrect) + { + lt = lt.GetTransformed(translatex, translatey, scale, -scale); + rb = rb.GetTransformed(translatex, translatey, scale, -scale); + } + + // Make quad + FlatQuad quad = new FlatQuad(PrimitiveType.TriangleStrip, lt.x, lt.y, rb.x, rb.y); + quad.SetColors(c.ToInt()); + + // Set renderstates for rendering + graphics.Device.SetRenderState(RenderState.CullMode, Cull.None); + graphics.Device.SetRenderState(RenderState.ZEnable, false); + graphics.Device.SetRenderState(RenderState.AlphaBlendEnable, false); + graphics.Device.SetRenderState(RenderState.AlphaTestEnable, false); + graphics.Device.SetRenderState(RenderState.TextureFactor, -1); + graphics.Device.SetRenderState(RenderState.FogEnable, false); + SetWorldTransformation(false); + graphics.Device.SetTexture(0, texture.Texture); + graphics.Shaders.Display2D.Texture1 = texture.Texture; + graphics.Shaders.Display2D.SetSettings(1f, 1f, 0f, 1f, General.Settings.ClassicBilinear); + + // Draw + graphics.Shaders.Display2D.Begin(); + graphics.Shaders.Display2D.BeginPass(1); + quad.Render(graphics); + graphics.Shaders.Display2D.EndPass(); + graphics.Shaders.Display2D.End(); + } + + // This renders a line with given color + public void RenderLine(Vector2D start, Vector2D end, float thickness, PixelColor c, bool transformcoords) + { + FlatVertex[] verts = new FlatVertex[4]; + + // Calculate positions + if (transformcoords) + { + start = start.GetTransformed(translatex, translatey, scale, -scale); + end = end.GetTransformed(translatex, translatey, scale, -scale); + } + + // Calculate offsets + Vector2D delta = end - start; + Vector2D dn = delta.GetNormal() * thickness; + + // Make vertices + verts[0].x = start.x - dn.x + dn.y; + verts[0].y = start.y - dn.y - dn.x; + verts[0].z = 0.0f; + verts[0].c = c.ToInt(); + verts[1].x = start.x - dn.x - dn.y; + verts[1].y = start.y - dn.y + dn.x; + verts[1].z = 0.0f; + verts[1].c = c.ToInt(); + verts[2].x = end.x + dn.x + dn.y; + verts[2].y = end.y + dn.y - dn.x; + verts[2].z = 0.0f; + verts[2].c = c.ToInt(); + verts[3].x = end.x + dn.x - dn.y; + verts[3].y = end.y + dn.y + dn.x; + verts[3].z = 0.0f; + verts[3].c = c.ToInt(); + + // Set renderstates for rendering + graphics.Device.SetRenderState(RenderState.CullMode, Cull.None); + graphics.Device.SetRenderState(RenderState.ZEnable, false); + graphics.Device.SetRenderState(RenderState.AlphaBlendEnable, false); + graphics.Device.SetRenderState(RenderState.AlphaTestEnable, false); + graphics.Device.SetRenderState(RenderState.TextureFactor, -1); + graphics.Device.SetRenderState(RenderState.FogEnable, false); + SetWorldTransformation(false); + graphics.Device.SetTexture(0, General.Map.Data.WhiteTexture.Texture); + graphics.Shaders.Display2D.Texture1 = General.Map.Data.WhiteTexture.Texture; + graphics.Shaders.Display2D.SetSettings(1f, 1f, 0f, 1f, General.Settings.ClassicBilinear); + + // Draw + graphics.Shaders.Display2D.Begin(); + graphics.Shaders.Display2D.BeginPass(0); + graphics.Device.DrawUserPrimitives(PrimitiveType.TriangleStrip, 0, 2, verts); + graphics.Shaders.Display2D.EndPass(); + graphics.Shaders.Display2D.End(); + } + + #endregion + + #region ================== Geometry + + // This renders the linedefs of a sector with special color + public void PlotSector(Sector s, PixelColor c) + { + // Go for all sides in the sector + foreach (Sidedef sd in s.Sidedefs) + { + // Render this linedef + PlotLinedef(sd.Line, c); + + // Render the two vertices on top + PlotVertex(sd.Line.Start, DetermineVertexColor(sd.Line.Start)); + PlotVertex(sd.Line.End, DetermineVertexColor(sd.Line.End)); + } + } + + // This renders the linedefs of a sector + public void PlotSector(Sector s) + { + // Go for all sides in the sector + foreach (Sidedef sd in s.Sidedefs) + { + // Render this linedef + PlotLinedef(sd.Line, DetermineLinedefColor(sd.Line)); + + // Render the two vertices on top + PlotVertex(sd.Line.Start, DetermineVertexColor(sd.Line.Start)); + PlotVertex(sd.Line.End, DetermineVertexColor(sd.Line.End)); + } + } + + // This renders a simple line + public void PlotLine(Vector2D start, Vector2D end, PixelColor c) + { + // Transform coordinates + Vector2D v1 = start.GetTransformed(translatex, translatey, scale, -scale); + Vector2D v2 = end.GetTransformed(translatex, translatey, scale, -scale); + + // Draw line + plotter.DrawLineSolid((int)v1.x, (int)v1.y, (int)v2.x, (int)v2.y, ref c); + } + + // This renders a single linedef + public void PlotLinedef(Linedef l, PixelColor c) + { + // Transform vertex coordinates + Vector2D v1 = l.Start.Position.GetTransformed(translatex, translatey, scale, -scale); + Vector2D v2 = l.End.Position.GetTransformed(translatex, translatey, scale, -scale); + + // Draw line + plotter.DrawLineSolid((int)v1.x, (int)v1.y, (int)v2.x, (int)v2.y, ref c); + + // Calculate normal indicator + float mx = (v2.x - v1.x) * 0.5f; + float my = (v2.y - v1.y) * 0.5f; + + // Draw normal indicator + plotter.DrawLineSolid((int)(v1.x + mx), (int)(v1.y + my), + (int)((v1.x + mx) - (my * l.LengthInv) * linenormalsize), + (int)((v1.y + my) + (mx * l.LengthInv) * linenormalsize), ref c); + } + + // This renders a set of linedefs + public void PlotLinedefSet(ICollection linedefs) + { + // Go for all linedefs + foreach (Linedef l in linedefs) PlotLinedef(l, DetermineLinedefColor(l)); + } + + // This renders a single vertex + public void PlotVertex(Vertex v, int colorindex) + { + // Transform vertex coordinates + Vector2D nv = v.Position.GetTransformed(translatex, translatey, scale, -scale); + + // Draw pixel here + plotter.DrawVertexSolid((int)nv.x, (int)nv.y, vertexsize, ref General.Colors.Colors[colorindex], ref General.Colors.BrightColors[colorindex], ref General.Colors.DarkColors[colorindex]); + } + + // This renders a single vertex at specified coordinates + public void PlotVertexAt(Vector2D v, int colorindex) + { + // Transform vertex coordinates + Vector2D nv = v.GetTransformed(translatex, translatey, scale, -scale); + + // Draw pixel here + plotter.DrawVertexSolid((int)nv.x, (int)nv.y, vertexsize, ref General.Colors.Colors[colorindex], ref General.Colors.BrightColors[colorindex], ref General.Colors.DarkColors[colorindex]); + } + + // This renders a set of vertices + public void PlotVerticesSet(ICollection vertices) + { + // Go for all vertices + foreach (Vertex v in vertices) PlotVertex(v, DetermineVertexColor(v)); + } + + #endregion + } +} diff --git a/Source/Core/Rendering/Renderer3D.cs b/Source/Core/Rendering/Renderer3D.cs new file mode 100644 index 0000000..19f8c51 --- /dev/null +++ b/Source/Core/Rendering/Renderer3D.cs @@ -0,0 +1,1008 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using System.Drawing; +using System.ComponentModel; +using SlimDX; +using CodeImp.DoomBuilder.Geometry; +using SlimDX.Direct3D9; +using CodeImp.DoomBuilder.Data; +using CodeImp.DoomBuilder.VisualModes; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.IO; // villsa + +#endregion + +namespace CodeImp.DoomBuilder.Rendering +{ + internal sealed class Renderer3D : Renderer, IRenderer3D + { + #region ================== Constants + + private const int RENDER_PASSES = 4; + private const float PROJ_NEAR_PLANE = 1f; + private const float CROSSHAIR_SCALE = 0.06f; + private const float FOG_RANGE = 0.9f; + + #endregion + + #region ================== Variables + + // Matrices + private Matrix projection; + private Matrix view3d; + private Matrix billboard; + private Matrix worldviewproj; + private Matrix view2d; + private Matrix world; + private Vector3D cameraposition; + private int shaderpass; + + private bool showlightonly; // villsa + + // Window size + private Size windowsize; + + // Frustum + private ProjectedFrustum2D frustum; + + // Thing cage + private VertexBuffer thingcage; + private bool renderthingcages; + + // villsa 9/15/11 (builder64) thing arrows + private VertexBuffer thingarrow; + + // Crosshair + private FlatVertex[] crosshairverts; + private bool crosshairbusy; + + // Highlighting + private IVisualPickable highlighted; + private float highlightglow; + private float highlightglowinv; + private ColorImage highlightimage; + private ColorImage selectionimage; + private bool showselection; + private bool showhighlight; + + // Geometry to be rendered. + // Each Dictionary in the array is a render pass. + // Each BinaryHeap in the Dictionary contains all geometry that needs + // to be rendered with the associated ImageData. + // The BinaryHeap sorts the geometry by sector to minimize stream switchs. + private Dictionary>[] geometry; + + // Things to be rendered. + // Each Dictionary in the array is a render pass. + // Each VisualThing is inserted in the Dictionary by their texture image. + private Dictionary>[] things; + + // Things to be rendered, sorted by distance from camera + private BinaryHeap thingsbydistance; + + #endregion + + #region ================== Properties + + public ProjectedFrustum2D Frustum2D { get { return frustum; } } + public bool DrawThingCages { get { return renderthingcages; } set { renderthingcages = value; } } + public bool ShowSelection { get { return showselection; } set { showselection = value; } } + public bool ShowHighlight { get { return showhighlight; } set { showhighlight = value; } } + public bool ShowLightOnly { get { return showlightonly; } set { showlightonly = value; } } // villsa + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + internal Renderer3D(D3DDevice graphics) : base(graphics) + { + // Initialize + CreateProjection(); + CreateMatrices2D(); + SetupThingCage(); + SetupThingArrow(); // villsa + SetupTextures(); + renderthingcages = true; + showselection = true; + showhighlight = true; + + // Dummy frustum + frustum = new ProjectedFrustum2D(new Vector2D(), 0.0f, 0.0f, PROJ_NEAR_PLANE, + General.Settings.ViewDistance, Angle2D.DegToRad((float)General.Settings.VisualFOV)); + + // We have no destructor + GC.SuppressFinalize(this); + } + + // Disposer + internal override void Dispose() + { + // Not already disposed? + if (!isdisposed) + { + // Clean up + if (thingcage != null) thingcage.Dispose(); + if (thingarrow != null) thingarrow.Dispose(); // villsa 9/15/11 + if (selectionimage != null) selectionimage.Dispose(); + if (highlightimage != null) highlightimage.Dispose(); + thingcage = null; + thingarrow = null; // villsa 9/15/11 + selectionimage = null; + highlightimage = null; + + // Done + base.Dispose(); + } + } + + #endregion + + #region ================== Management + + // This is called before a device is reset + // (when resized or display adapter was changed) + public override void UnloadResource() + { + crosshairverts = null; + if (thingcage != null) thingcage.Dispose(); + if (thingarrow != null) thingarrow.Dispose(); // villsa 9/15/11 + if (selectionimage != null) selectionimage.Dispose(); + if (highlightimage != null) highlightimage.Dispose(); + thingcage = null; + thingarrow = null; // villsa 9/15/11 + selectionimage = null; + highlightimage = null; + } + + // This is called resets when the device is reset + // (when resized or display adapter was changed) + public override void ReloadResource() + { + CreateMatrices2D(); + SetupThingCage(); + SetupThingArrow(); // villsa + SetupTextures(); + } + + // This makes screen vertices for display + private void CreateCrosshairVerts(Size texturesize) + { + // Determine coordinates + float width = (float)windowsize.Width; + float height = (float)windowsize.Height; + float size = (float)height * CROSSHAIR_SCALE; + RectangleF rect = new RectangleF((width - size) / 2, (height - size) / 2, size, size); + + // Make vertices + crosshairverts = new FlatVertex[4]; + crosshairverts[0].x = rect.Left; + crosshairverts[0].y = rect.Top; + crosshairverts[0].c = -1; + crosshairverts[0].u = 1f / texturesize.Width; + crosshairverts[0].v = 1f / texturesize.Height; + crosshairverts[1].x = rect.Right; + crosshairverts[1].y = rect.Top; + crosshairverts[1].c = -1; + crosshairverts[1].u = 1f - 1f / texturesize.Width; + crosshairverts[1].v = 1f / texturesize.Height; + crosshairverts[2].x = rect.Left; + crosshairverts[2].y = rect.Bottom; + crosshairverts[2].c = -1; + crosshairverts[2].u = 1f / texturesize.Width; + crosshairverts[2].v = 1f - 1f / texturesize.Height; + crosshairverts[3].x = rect.Right; + crosshairverts[3].y = rect.Bottom; + crosshairverts[3].c = -1; + crosshairverts[3].u = 1f - 1f / texturesize.Width; + crosshairverts[3].v = 1f - 1f / texturesize.Height; + } + + #endregion + + #region ================== Resources + + // This loads the textures for highlight and selection if we need them + private void SetupTextures() + { + if (!graphics.Shaders.Enabled) + { + highlightimage = new ColorImage(General.Colors.Highlight, 32, 32); + highlightimage.LoadImage(); + highlightimage.CreateTexture(); + + selectionimage = new ColorImage(General.Colors.Selection, 32, 32); + selectionimage.LoadImage(); + selectionimage.CreateTexture(); + } + } + + // villsa 9/15/11 + private void SetupThingArrow() + { + const int totalvertices = 6; + WorldVertex[] tv = new WorldVertex[totalvertices]; + + tv[0] = new WorldVertex(0.0f, 0.0f, 0.5f); + tv[1] = new WorldVertex(0.0f, 1.35f, 0.5f); + tv[2] = new WorldVertex(0.0f, 1.35f, 0.5f); + tv[3] = new WorldVertex(0.175f, 1.1f, 0.5f); + tv[4] = new WorldVertex(0.0f, 1.35f, 0.5f); + tv[5] = new WorldVertex(-0.175f, 1.1f, 0.5f); + + // Create vertexbuffer + thingarrow = new VertexBuffer(General.Map.Graphics.Device, WorldVertex.Stride * totalvertices, + Usage.WriteOnly | Usage.Dynamic, VertexFormat.None, Pool.Default); + DataStream bufferstream = thingarrow.Lock(0, WorldVertex.Stride * totalvertices, LockFlags.Discard); + bufferstream.WriteRange(tv); + thingarrow.Unlock(); + bufferstream.Dispose(); + } + + // This sets up the thing cage + private void SetupThingCage() + { + const int totalvertices = 36; + WorldVertex[] tv = new WorldVertex[totalvertices]; + float x0 = -1.0f; + float x1 = 1.0f; + float y0 = -1.0f; + float y1 = 1.0f; + float z0 = 0.0f; + float z1 = 1.0f; + float u0 = 0.0f; + float u1 = 1.0f; + float v0 = 0.0f; + float v1 = 1.0f; + int c = -1; + + // Front + tv[0] = new WorldVertex(x0, y0, z0, c, u0, v0); + tv[1] = new WorldVertex(x0, y0, z1, c, u0, v1); + tv[2] = new WorldVertex(x1, y0, z0, c, u1, v0); + tv[3] = new WorldVertex(x1, y0, z0, c, u1, v0); + tv[4] = new WorldVertex(x0, y0, z1, c, u0, v1); + tv[5] = new WorldVertex(x1, y0, z1, c, u1, v1); + + // Right + tv[6] = new WorldVertex(x1, y0, z0, c, u0, v0); + tv[7] = new WorldVertex(x1, y0, z1, c, u0, v1); + tv[8] = new WorldVertex(x1, y1, z0, c, u1, v0); + tv[9] = new WorldVertex(x1, y1, z0, c, u1, v0); + tv[10] = new WorldVertex(x1, y0, z1, c, u0, v1); + tv[11] = new WorldVertex(x1, y1, z1, c, u1, v1); + + // Back + tv[12] = new WorldVertex(x1, y1, z0, c, u0, v0); + tv[13] = new WorldVertex(x1, y1, z1, c, u0, v1); + tv[14] = new WorldVertex(x0, y1, z0, c, u1, v0); + tv[15] = new WorldVertex(x0, y1, z0, c, u1, v0); + tv[16] = new WorldVertex(x1, y1, z1, c, u0, v1); + tv[17] = new WorldVertex(x0, y1, z1, c, u1, v1); + + // Left + tv[18] = new WorldVertex(x0, y1, z0, c, u0, v1); + tv[19] = new WorldVertex(x0, y1, z1, c, u0, v0); + tv[20] = new WorldVertex(x0, y0, z1, c, u1, v0); + tv[21] = new WorldVertex(x0, y1, z0, c, u1, v0); + tv[22] = new WorldVertex(x0, y0, z1, c, u0, v1); + tv[23] = new WorldVertex(x0, y0, z0, c, u1, v1); + + // Top + tv[24] = new WorldVertex(x0, y0, z1, c, u0, v0); + tv[25] = new WorldVertex(x0, y1, z1, c, u0, v1); + tv[26] = new WorldVertex(x1, y0, z1, c, u1, v0); + tv[27] = new WorldVertex(x1, y0, z1, c, u1, v0); + tv[28] = new WorldVertex(x0, y1, z1, c, u0, v1); + tv[29] = new WorldVertex(x1, y1, z1, c, u1, v1); + + // Bottom + tv[30] = new WorldVertex(x1, y0, z0, c, u1, v0); + tv[31] = new WorldVertex(x0, y1, z0, c, u0, v1); + tv[32] = new WorldVertex(x0, y0, z0, c, u0, v0); + tv[33] = new WorldVertex(x1, y0, z0, c, u1, v0); + tv[34] = new WorldVertex(x1, y1, z0, c, u1, v1); + tv[35] = new WorldVertex(x0, y1, z0, c, u0, v1); + + // Create vertexbuffer + thingcage = new VertexBuffer(General.Map.Graphics.Device, WorldVertex.Stride * totalvertices, + Usage.WriteOnly | Usage.Dynamic, VertexFormat.None, Pool.Default); + DataStream bufferstream = thingcage.Lock(0, WorldVertex.Stride * totalvertices, LockFlags.Discard); + bufferstream.WriteRange(tv); + thingcage.Unlock(); + bufferstream.Dispose(); + } + + #endregion + + #region ================== Presentation + + // This creates the projection + internal void CreateProjection() + { + // Calculate aspect + float aspect = (float)General.Map.Graphics.RenderTarget.ClientSize.Width / + (float)General.Map.Graphics.RenderTarget.ClientSize.Height; + + // The DirectX PerspectiveFovRH matrix method calculates the scaling in X and Y as follows: + // yscale = 1 / tan(fovY / 2) + // xscale = yscale / aspect + // The fov specified in the method is the FOV over Y, but we want the user to specify the FOV + // over X, so calculate what it would be over Y first; + float fov = Angle2D.DegToRad((float)General.Settings.VisualFOV); + float reversefov = 1.0f / (float)Math.Tan(fov / 2.0f); + float reversefovy = reversefov * aspect; + float fovy = (float)Math.Atan(1.0f / reversefovy) * 2.0f; + + // Make the projection matrix + projection = Matrix.PerspectiveFovRH(fovy, aspect, PROJ_NEAR_PLANE, General.Settings.ViewDistance); + + // Apply matrices + ApplyMatrices3D(); + } + + // This creates matrices for a camera view + public void PositionAndLookAt(Vector3D pos, Vector3D lookat) + { + Vector3D delta; + float anglexy, anglez; + + // Calculate delta vector + cameraposition = pos; + delta = lookat - pos; + anglexy = delta.GetAngleXY(); + anglez = delta.GetAngleZ(); + + // Create frustum + frustum = new ProjectedFrustum2D(pos, anglexy, anglez, PROJ_NEAR_PLANE, + General.Settings.ViewDistance, Angle2D.DegToRad((float)General.Settings.VisualFOV)); + + // Make the view matrix + view3d = Matrix.LookAtRH(D3DDevice.V3(pos), D3DDevice.V3(lookat), new Vector3(0f, 0f, 1f)); + + // Make the billboard matrix + billboard = Matrix.RotationZ(anglexy + Angle2D.PI); + } + + // This creates 2D view matrix + private void CreateMatrices2D() + { + windowsize = graphics.RenderTarget.ClientSize; + Matrix scaling = Matrix.Scaling((1f / (float)windowsize.Width) * 2f, (1f / (float)windowsize.Height) * -2f, 1f); + Matrix translate = Matrix.Translation(-(float)windowsize.Width * 0.5f, -(float)windowsize.Height * 0.5f, 0f); + view2d = Matrix.Multiply(translate, scaling); + } + + // This applies the matrices + private void ApplyMatrices3D() + { + worldviewproj = world * view3d * projection; + graphics.Shaders.World3D.WorldViewProj = worldviewproj; + graphics.Device.SetTransform(TransformState.World, world); + graphics.Device.SetTransform(TransformState.Projection, projection); + graphics.Device.SetTransform(TransformState.View, view3d); + } + + // This sets the appropriate view matrix + public void ApplyMatrices2D() + { + graphics.Device.SetTransform(TransformState.World, world); + graphics.Device.SetTransform(TransformState.Projection, Matrix.Identity); + graphics.Device.SetTransform(TransformState.View, view2d); + } + + #endregion + + #region ================== Start / Finish + + // This starts rendering + public bool Start() + { + // Create texture + if (General.Map.Data.ThingBox.Texture == null) + General.Map.Data.ThingBox.CreateTexture(); + + // Start drawing + if (graphics.StartRendering(true, General.Colors.Background.ToColorValue(), graphics.BackBuffer, graphics.DepthBuffer)) + { + // Beginning renderstates + graphics.Device.SetRenderState(RenderState.CullMode, Cull.None); + graphics.Device.SetRenderState(RenderState.ZEnable, false); + graphics.Device.SetRenderState(RenderState.AlphaBlendEnable, false); + graphics.Device.SetRenderState(RenderState.AlphaTestEnable, false); + graphics.Device.SetRenderState(RenderState.SourceBlend, Blend.SourceAlpha); + graphics.Device.SetRenderState(RenderState.DestinationBlend, Blend.InverseSourceAlpha); + graphics.Device.SetRenderState(RenderState.FogEnable, false); + graphics.Device.SetRenderState(RenderState.FogDensity, 1.0f); + graphics.Device.SetRenderState(RenderState.FogColor, General.Colors.Background.ToInt()); + graphics.Device.SetRenderState(RenderState.FogStart, General.Settings.ViewDistance * FOG_RANGE); + graphics.Device.SetRenderState(RenderState.FogEnd, General.Settings.ViewDistance); + graphics.Device.SetRenderState(RenderState.FogTableMode, FogMode.Linear); + graphics.Device.SetRenderState(RenderState.RangeFogEnable, false); + graphics.Device.SetRenderState(RenderState.TextureFactor, -1); + graphics.Shaders.World3D.SetModulateColor(-1); + graphics.Shaders.World3D.SetHighlightColor(0); + + // Texture addressing + graphics.Device.SetSamplerState(0, SamplerState.AddressU, TextureAddress.Wrap); + graphics.Device.SetSamplerState(0, SamplerState.AddressV, TextureAddress.Wrap); + graphics.Device.SetSamplerState(0, SamplerState.AddressW, TextureAddress.Wrap); + + // Matrices + world = Matrix.Identity; + ApplyMatrices3D(); + + // Highlight + if (General.Settings.AnimateVisualSelection) + { + double time = General.stopwatch.Elapsed.TotalMilliseconds; + highlightglow = (float)Math.Sin(time / 100.0f) * 0.1f + 0.4f; + highlightglowinv = -(float)Math.Sin(time / 100.0f) * 0.1f + 0.4f; + } + else + { + highlightglow = 0.4f; + highlightglowinv = 0.3f; + } + + // Determine shader pass to use + if (fullbrightness) shaderpass = 1; else shaderpass = 0; + + // Create crosshair vertices + if (crosshairverts == null) + CreateCrosshairVerts(new Size(General.Map.Data.Crosshair3D.Width, General.Map.Data.Crosshair3D.Height)); + + // Ready + return true; + } + else + { + // Can't render now + return false; + } + } + + // This begins rendering world geometry + public void StartGeometry() + { + // Make collection + geometry = new Dictionary>[RENDER_PASSES]; + things = new Dictionary>[RENDER_PASSES]; + thingsbydistance = new BinaryHeap(); + for (int i = 0; i < RENDER_PASSES; i++) + { + geometry[i] = new Dictionary>(); + things[i] = new Dictionary>(); + } + } + + // This ends rendering world geometry + public void FinishGeometry() + { + // Initial renderstates + graphics.Device.SetRenderState(RenderState.CullMode, Cull.Counterclockwise); + graphics.Device.SetRenderState(RenderState.ZEnable, true); + graphics.Device.SetRenderState(RenderState.ZWriteEnable, true); + graphics.Device.SetRenderState(RenderState.AlphaBlendEnable, false); + graphics.Device.SetRenderState(RenderState.AlphaTestEnable, false); + graphics.Device.SetRenderState(RenderState.TextureFactor, -1); + graphics.Shaders.World3D.Begin(); + + // SOLID PASS + world = Matrix.Identity; + ApplyMatrices3D(); + RenderSinglePass((int)RenderPass.Solid); + + // MASK PASS + world = Matrix.Identity; + ApplyMatrices3D(); + graphics.Device.SetRenderState(RenderState.AlphaTestEnable, true); + RenderSinglePass((int)RenderPass.Mask); + + // ALPHA PASS + world = Matrix.Identity; + ApplyMatrices3D(); + graphics.Device.SetRenderState(RenderState.AlphaBlendEnable, true); + graphics.Device.SetRenderState(RenderState.AlphaTestEnable, false); + graphics.Device.SetRenderState(RenderState.ZWriteEnable, false); + graphics.Device.SetRenderState(RenderState.SourceBlend, Blend.SourceAlpha); + graphics.Device.SetRenderState(RenderState.DestinationBlend, Blend.InverseSourceAlpha); + RenderSinglePass((int)RenderPass.Alpha); + + // THINGS + if (renderthingcages) RenderThingCages(); + RenderThingArrows(); // villsa + + // ADDITIVE PASS + world = Matrix.Identity; + ApplyMatrices3D(); + graphics.Device.SetRenderState(RenderState.DestinationBlend, Blend.One); + RenderSinglePass((int)RenderPass.Additive); + + // Remove references + graphics.Shaders.World3D.Texture1 = null; + + // Done + graphics.Shaders.World3D.End(); + geometry = null; + } + + // villsa 9/15/11 + private void RenderThingArrows() + { + int currentshaderpass = shaderpass; + int highshaderpass = shaderpass + 2; + + // Set renderstates + graphics.Device.SetRenderState(RenderState.AlphaBlendEnable, true); + graphics.Device.SetRenderState(RenderState.AlphaTestEnable, false); + graphics.Device.SetRenderState(RenderState.ZWriteEnable, false); + graphics.Device.SetRenderState(RenderState.SourceBlend, Blend.SourceAlpha); + graphics.Device.SetRenderState(RenderState.DestinationBlend, Blend.InverseSourceAlpha); + graphics.Device.SetStreamSource(0, thingarrow, 0, WorldVertex.Stride); + graphics.Device.SetTexture(0, General.Map.Data.WhiteTexture.Texture); + graphics.Shaders.World3D.Texture1 = General.Map.Data.WhiteTexture.Texture; + + graphics.Shaders.World3D.BeginPass(shaderpass); + foreach (VisualThing t in thingsbydistance) + { + // Determine the shader pass we want to use for this object + int wantedshaderpass = (((t == highlighted) && showhighlight) || (t.Selected && showselection)) ? highshaderpass : shaderpass; + + // Switch shader pass? + if (currentshaderpass != wantedshaderpass) + { + graphics.Shaders.World3D.EndPass(); + graphics.Shaders.World3D.BeginPass(wantedshaderpass); + currentshaderpass = wantedshaderpass; + } + + // Setup matrix + world = Matrix.Multiply(t.CageScales, t.Position); + Matrix rot = Matrix.RotationYawPitchRoll(0.0f, 0.0f, t.Thing.Angle + Angle2D.PI); + world = Matrix.Multiply(rot, world); + ApplyMatrices3D(); + + // Setup color + if (currentshaderpass == highshaderpass) + { + Color4 highcolor = CalculateHighlightColor((t == highlighted) && showhighlight, t.Selected && showselection); + graphics.Shaders.World3D.SetHighlightColor(highcolor.ToArgb()); + highcolor.Alpha = 1.0f; + graphics.Shaders.World3D.SetModulateColor(highcolor.ToArgb()); + graphics.Device.SetRenderState(RenderState.TextureFactor, highcolor.ToArgb()); + } + else + { + graphics.Shaders.World3D.SetModulateColor(t.CageColor); + graphics.Device.SetRenderState(RenderState.TextureFactor, t.CageColor); + } + + // Render! + graphics.Shaders.World3D.ApplySettings(); + graphics.Device.DrawPrimitives(PrimitiveType.LineList, 0, 3); + } + + // Done + graphics.Shaders.World3D.EndPass(); + graphics.Shaders.World3D.SetModulateColor(-1); + graphics.Device.SetRenderState(RenderState.TextureFactor, -1); + } + + // This renders all thing cages + private void RenderThingCages() + { + int currentshaderpass = shaderpass; + int highshaderpass = shaderpass + 2; + + // Set renderstates + graphics.Device.SetRenderState(RenderState.AlphaBlendEnable, true); + graphics.Device.SetRenderState(RenderState.AlphaTestEnable, false); + graphics.Device.SetRenderState(RenderState.ZWriteEnable, false); + graphics.Device.SetRenderState(RenderState.SourceBlend, Blend.SourceAlpha); + graphics.Device.SetRenderState(RenderState.DestinationBlend, Blend.InverseSourceAlpha); + graphics.Device.SetStreamSource(0, thingcage, 0, WorldVertex.Stride); + graphics.Device.SetTexture(0, General.Map.Data.ThingBox.Texture); + graphics.Shaders.World3D.Texture1 = General.Map.Data.ThingBox.Texture; + + graphics.Shaders.World3D.BeginPass(shaderpass); + foreach (VisualThing t in thingsbydistance) + { + // Determine the shader pass we want to use for this object + int wantedshaderpass = (((t == highlighted) && showhighlight) || (t.Selected && showselection)) ? highshaderpass : shaderpass; + + // Switch shader pass? + if (currentshaderpass != wantedshaderpass) + { + graphics.Shaders.World3D.EndPass(); + graphics.Shaders.World3D.BeginPass(wantedshaderpass); + currentshaderpass = wantedshaderpass; + } + + // Setup matrix + world = Matrix.Multiply(t.CageScales, t.Position); + ApplyMatrices3D(); + + // Setup color + if (currentshaderpass == highshaderpass) + { + Color4 highcolor = CalculateHighlightColor((t == highlighted) && showhighlight, t.Selected && showselection); + graphics.Shaders.World3D.SetHighlightColor(highcolor.ToArgb()); + highcolor.Alpha = 1.0f; + graphics.Shaders.World3D.SetModulateColor(highcolor.ToArgb()); + graphics.Device.SetRenderState(RenderState.TextureFactor, highcolor.ToArgb()); + } + else + { + graphics.Shaders.World3D.SetModulateColor(t.CageColor); + graphics.Device.SetRenderState(RenderState.TextureFactor, t.CageColor); + } + + // Render! + graphics.Shaders.World3D.ApplySettings(); + graphics.Device.DrawPrimitives(PrimitiveType.TriangleList, 0, 12); + } + + // Done + graphics.Shaders.World3D.EndPass(); + graphics.Shaders.World3D.SetModulateColor(-1); + graphics.Device.SetRenderState(RenderState.TextureFactor, -1); + } + + // This performs a single render pass + private void RenderSinglePass(int pass) + { + int currentshaderpass = shaderpass; + int highshaderpass = shaderpass + 2; + + // Get geometry for this pass + Dictionary> geopass = geometry[pass]; + + // Begin rendering with this shader + graphics.Shaders.World3D.BeginPass(shaderpass); + + // Render the geometry collected + foreach (KeyValuePair> group in geopass) + { + ImageData curtexture; + + // What texture to use? + if (group.Key is UnknownImage) + curtexture = General.Map.Data.UnknownTexture3D; + else if ((group.Key != null) && group.Key.IsImageLoaded && !group.Key.IsDisposed) + curtexture = group.Key; + else + curtexture = General.Map.Data.Hourglass3D; + + // Create Direct3D texture if still needed + if ((curtexture.Texture == null) || curtexture.Texture.Disposed) + curtexture.CreateTexture(); + + // Apply texture + if (showlightonly) // villsa + { + if (!graphics.Shaders.Enabled) graphics.Device.SetTexture(0, General.Map.Data.WhiteTexture.Texture); + graphics.Shaders.World3D.Texture1 = General.Map.Data.WhiteTexture.Texture; + } + else + { + if (!graphics.Shaders.Enabled) graphics.Device.SetTexture(0, curtexture.Texture); + graphics.Shaders.World3D.Texture1 = curtexture.Texture; + } + + // Go for all geometry that uses this texture + VisualSector sector = null; + foreach (VisualGeometry g in group.Value) + { + // Changing sector? + if (!object.ReferenceEquals(g.Sector, sector)) + { + // Update the sector if needed + if (g.Sector.NeedsUpdateGeo) g.Sector.Update(); + + // Only do this sector when a vertexbuffer is created + if (g.Sector.GeometryBuffer != null) + { + // Change current sector + sector = g.Sector; + + // Set stream source + graphics.Device.SetStreamSource(0, sector.GeometryBuffer, 0, WorldVertex.Stride); + } + else + { + sector = null; + } + } + + if (sector != null) + { + // Determine the shader pass we want to use for this object + int wantedshaderpass = (((g == highlighted) && showhighlight) || (g.Selected && showselection)) ? highshaderpass : shaderpass; + + // Switch shader pass? + if (currentshaderpass != wantedshaderpass) + { + graphics.Shaders.World3D.EndPass(); + graphics.Shaders.World3D.BeginPass(wantedshaderpass); + currentshaderpass = wantedshaderpass; + } + + // villsa - mirror UVs if special flags are set + if (General.Map.FormatInterface.InDoom64Mode) + { + if (g.Sidedef != null) + { + // villsa - fixed 9/25/11 + if (g.Sidedef.Line != null) + { + if (g.Sidedef.Line.IsFlagSet("1073741824")) + graphics.Device.SetSamplerState(0, SamplerState.AddressU, TextureAddress.Mirror); + else + graphics.Device.SetSamplerState(0, SamplerState.AddressU, TextureAddress.Wrap); + + if (g.Sidedef.Line.IsFlagSet("2147483648")) + graphics.Device.SetSamplerState(0, SamplerState.AddressV, TextureAddress.Mirror); + else + graphics.Device.SetSamplerState(0, SamplerState.AddressV, TextureAddress.Wrap); + } + } + } + + // Set the colors to use + if (!graphics.Shaders.Enabled) + { + graphics.Device.SetTexture(2, (g.Selected && showselection) ? selectionimage.Texture : null); + graphics.Device.SetTexture(3, ((g == highlighted) && showhighlight) ? highlightimage.Texture : null); + } + else + { + graphics.Shaders.World3D.SetHighlightColor(CalculateHighlightColor((g == highlighted) && showhighlight, (g.Selected && showselection)).ToArgb()); + graphics.Shaders.World3D.ApplySettings(); + } + + // Render! + graphics.Device.DrawPrimitives(PrimitiveType.TriangleList, g.VertexOffset, g.Triangles); + + // villsa - reset samplers to default wrappings + if (General.Map.FormatInterface.InDoom64Mode) + { + graphics.Device.SetSamplerState(0, SamplerState.AddressU, TextureAddress.Wrap); + graphics.Device.SetSamplerState(0, SamplerState.AddressV, TextureAddress.Wrap); + } + } + } + } + + // Get things for this pass + Dictionary> thingspass = things[pass]; + if (thingspass.Count > 0) + { + // Texture addressing + graphics.Device.SetSamplerState(0, SamplerState.AddressU, TextureAddress.Clamp); + graphics.Device.SetSamplerState(0, SamplerState.AddressV, TextureAddress.Clamp); + graphics.Device.SetSamplerState(0, SamplerState.AddressW, TextureAddress.Clamp); + + // Render things collected + foreach (KeyValuePair> group in thingspass) + { + ImageData curtexture; + + if (!(group.Key is UnknownImage)) + { + // What texture to use? + if ((group.Key != null) && group.Key.IsImageLoaded && !group.Key.IsDisposed) + curtexture = group.Key; + else + curtexture = General.Map.Data.Hourglass3D; + + // Create Direct3D texture if still needed + if ((curtexture.Texture == null) || curtexture.Texture.Disposed) + curtexture.CreateTexture(); + + // Apply texture + if (!graphics.Shaders.Enabled) graphics.Device.SetTexture(0, curtexture.Texture); + graphics.Shaders.World3D.Texture1 = curtexture.Texture; + + // Render all things with this texture + foreach (VisualThing t in group.Value) + { + // Update buffer if needed + t.Update(); + + // Only do this sector when a vertexbuffer is created + if (t.GeometryBuffer != null) + { + // Determine the shader pass we want to use for this object + int wantedshaderpass = (((t == highlighted) && showhighlight) || (t.Selected && showselection)) ? highshaderpass : shaderpass; + + // Switch shader pass? + if (currentshaderpass != wantedshaderpass) + { + graphics.Shaders.World3D.EndPass(); + graphics.Shaders.World3D.BeginPass(wantedshaderpass); + currentshaderpass = wantedshaderpass; + } + + // Set the colors to use + if (!graphics.Shaders.Enabled) + { + graphics.Device.SetTexture(2, (t.Selected && showselection) ? selectionimage.Texture : null); + graphics.Device.SetTexture(3, ((t == highlighted) && showhighlight) ? highlightimage.Texture : null); + } + else + { + graphics.Shaders.World3D.SetHighlightColor(CalculateHighlightColor((t == highlighted) && showhighlight, (t.Selected && showselection)).ToArgb()); + } + + // Create the matrix for positioning / rotation + world = t.Orientation; + if (t.Billboard) world = Matrix.Multiply(world, billboard); + world = Matrix.Multiply(world, t.Position); + ApplyMatrices3D(); + graphics.Shaders.World3D.ApplySettings(); + + // Apply buffer + graphics.Device.SetStreamSource(0, t.GeometryBuffer, 0, WorldVertex.Stride); + + // Render! + graphics.Device.DrawPrimitives(PrimitiveType.TriangleList, 0, t.Triangles); + } + } + } + } + + // Texture addressing + graphics.Device.SetSamplerState(0, SamplerState.AddressU, TextureAddress.Wrap); + graphics.Device.SetSamplerState(0, SamplerState.AddressV, TextureAddress.Wrap); + graphics.Device.SetSamplerState(0, SamplerState.AddressW, TextureAddress.Wrap); + } + + // Done rendering with this shader + graphics.Shaders.World3D.EndPass(); + } + + // This calculates the highlight/selection color + public Color4 CalculateHighlightColor(bool ishighlighted, bool isselected) + { + Color4 highlightcolor = isselected ? General.Colors.Selection.ToColorValue() : General.Colors.Highlight.ToColorValue(); + highlightcolor.Alpha = ishighlighted ? highlightglowinv : highlightglow; + return highlightcolor; + } + + // This finishes rendering + public void Finish() + { + // Done + graphics.FinishRendering(); + graphics.Present(); + highlighted = null; + } + + #endregion + + #region ================== Rendering + + // This sets the highlighted object for the rendering + public void SetHighlightedObject(IVisualPickable obj) + { + highlighted = obj; + } + + // This collects a visual sector's geometry for rendering + public void AddSectorGeometry(VisualGeometry g) + { + // Must have a texture and vertices + if ((g.Texture != null) && (g.Triangles > 0)) + { + // Texture group not yet collected? + if (!geometry[g.RenderPassInt].ContainsKey(g.Texture)) + { + // Create texture group + geometry[g.RenderPassInt].Add(g.Texture, new BinaryHeap()); + } + + // Add geometry to texture group + geometry[g.RenderPassInt][g.Texture].Add(g); + } + } + + // This collects a visual sector's geometry for rendering + public void AddThingGeometry(VisualThing t) + { + // Make sure the distance to camera is calculated + t.CalculateCameraDistance(cameraposition); + thingsbydistance.Add(t); + + // Must have a texture! + if (t.Texture != null) + { + // Texture group not yet collected? + if (!things[t.RenderPassInt].ContainsKey(t.Texture)) + { + // Create texture group + things[t.RenderPassInt].Add(t.Texture, new List()); + } + + // Add geometry to texture group + things[t.RenderPassInt][t.Texture].Add(t); + } + } + + // This renders the crosshair + public void RenderCrosshair() + { + // Set renderstates + graphics.Device.SetRenderState(RenderState.CullMode, Cull.None); + graphics.Device.SetRenderState(RenderState.ZEnable, false); + graphics.Device.SetRenderState(RenderState.AlphaBlendEnable, true); + graphics.Device.SetRenderState(RenderState.AlphaTestEnable, false); + graphics.Device.SetRenderState(RenderState.SourceBlend, Blend.SourceAlpha); + graphics.Device.SetRenderState(RenderState.DestinationBlend, Blend.InverseSourceAlpha); + graphics.Device.SetRenderState(RenderState.TextureFactor, -1); + graphics.Device.SetTransform(TransformState.World, Matrix.Identity); + graphics.Device.SetTransform(TransformState.Projection, Matrix.Identity); + ApplyMatrices2D(); + + // Texture + if (crosshairbusy) + { + if (General.Map.Data.CrosshairBusy3D.Texture == null) General.Map.Data.CrosshairBusy3D.CreateTexture(); + graphics.Device.SetTexture(0, General.Map.Data.CrosshairBusy3D.Texture); + graphics.Shaders.Display2D.Texture1 = General.Map.Data.CrosshairBusy3D.Texture; + } + else + { + if (General.Map.Data.Crosshair3D.Texture == null) General.Map.Data.Crosshair3D.CreateTexture(); + graphics.Device.SetTexture(0, General.Map.Data.Crosshair3D.Texture); + graphics.Shaders.Display2D.Texture1 = General.Map.Data.Crosshair3D.Texture; + } + + // Draw + graphics.Shaders.Display2D.Begin(); + graphics.Shaders.Display2D.SetSettings(1.0f, 1.0f, 0.0f, 1.0f, true); + graphics.Shaders.Display2D.BeginPass(1); + graphics.Device.DrawUserPrimitives(PrimitiveType.TriangleStrip, 0, 2, crosshairverts); + graphics.Shaders.Display2D.EndPass(); + graphics.Shaders.Display2D.End(); + } + + // This switches fog on and off + public void SetFogMode(bool usefog) + { + graphics.Device.SetRenderState(RenderState.FogEnable, usefog); + } + + // This siwtches crosshair busy icon on and off + public void SetCrosshairBusy(bool busy) + { + crosshairbusy = busy; + } + + #endregion + } +} diff --git a/Source/Core/Rendering/ShaderManager.cs b/Source/Core/Rendering/ShaderManager.cs new file mode 100644 index 0000000..18b139b --- /dev/null +++ b/Source/Core/Rendering/ShaderManager.cs @@ -0,0 +1,142 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using System.Drawing; +using System.ComponentModel; +using CodeImp.DoomBuilder.Map; +using SlimDX.Direct3D9; +using SlimDX; +using CodeImp.DoomBuilder.Geometry; +using System.Drawing.Imaging; + +#endregion + +namespace CodeImp.DoomBuilder.Rendering +{ + internal class ShaderManager : ID3DResource + { + #region ================== Constants + + #endregion + + #region ================== Variables + + // Settings + private string shadertechnique; + private bool useshaders; + + // Shaders + private Display2DShader display2dshader; + private Things2DShader things2dshader; + private World3DShader world3dshader; + + // Device + private D3DDevice device; + + // Disposing + private bool isdisposed = false; + + #endregion + + #region ================== Properties + + public bool Enabled { get { return useshaders; } } + public string ShaderTechnique { get { return shadertechnique; } } + public Display2DShader Display2D { get { return display2dshader; } } + public Things2DShader Things2D { get { return things2dshader; } } + public World3DShader World3D { get { return world3dshader; } } + public bool IsDisposed { get { return isdisposed; } } + internal D3DDevice D3DDevice { get { return device; } } + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + public ShaderManager(D3DDevice device) + { + // Initialize + this.device = device; + + // Load + ReloadResource(); + + // Register as resource + device.RegisterResource(this); + + // We have no destructor + GC.SuppressFinalize(this); + } + + // Disposer + public void Dispose() + { + // Not already disposed? + if (!isdisposed) + { + // Clean up + UnloadResource(); + + // Unregister as resource + device.UnregisterResource(this); + + // Done + device = null; + isdisposed = true; + } + } + + #endregion + + #region ================== Resources + + // Clean up resources + public void UnloadResource() + { + display2dshader.Dispose(); + things2dshader.Dispose(); + world3dshader.Dispose(); + } + + // Load resources + public void ReloadResource() + { + Capabilities caps; + + // Check if we can use shaders + caps = General.Map.Graphics.Device.Capabilities; + useshaders = (caps.PixelShaderVersion.Major >= 2); + shadertechnique = "SM20"; + + // Initialize effects + display2dshader = new Display2DShader(this); + things2dshader = new Things2DShader(this); + world3dshader = new World3DShader(this); + } + + #endregion + } +} diff --git a/Source/Core/Rendering/SurfaceBufferSet.cs b/Source/Core/Rendering/SurfaceBufferSet.cs new file mode 100644 index 0000000..387038f --- /dev/null +++ b/Source/Core/Rendering/SurfaceBufferSet.cs @@ -0,0 +1,59 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using System.Drawing; +using System.ComponentModel; +using CodeImp.DoomBuilder.Map; +using SlimDX.Direct3D9; +using SlimDX; +using CodeImp.DoomBuilder.Geometry; +using System.Drawing.Imaging; +using CodeImp.DoomBuilder.Data; +using CodeImp.DoomBuilder.Editing; + +using Configuration = CodeImp.DoomBuilder.IO.Configuration; + +#endregion + +namespace CodeImp.DoomBuilder.Rendering +{ + internal struct SurfaceBufferSet + { + // The number of vertices per sector that this set is for + public int numvertices; + + // These are the vertex buffers. They are hashed by an integer key which + // is the number of vertices per sector geometry the buffer if meant for. + public List buffers; + public List buffersizes; + + // These are the entries that contain information for the contents of the buffers. + public List entries; + + // These are the empty entries in buferrs that are available. + public List holes; + } +} diff --git a/Source/Core/Rendering/SurfaceEntry.cs b/Source/Core/Rendering/SurfaceEntry.cs new file mode 100644 index 0000000..e015d27 --- /dev/null +++ b/Source/Core/Rendering/SurfaceEntry.cs @@ -0,0 +1,105 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using System.Drawing; +using System.ComponentModel; +using CodeImp.DoomBuilder.Map; +using SlimDX.Direct3D9; +using SlimDX; +using CodeImp.DoomBuilder.Geometry; +using System.Drawing.Imaging; +using CodeImp.DoomBuilder.Data; +using CodeImp.DoomBuilder.Editing; + +using Configuration = CodeImp.DoomBuilder.IO.Configuration; + +#endregion + +namespace CodeImp.DoomBuilder.Rendering +{ + // This is an entry is the surface manager and contains the information + // needed for a sector to place it's ceiling and floor surface geometry + // in a vertexbuffer. Sectors keep a reference to this entry to tell the + // surface manager to remove them if needed. + internal class SurfaceEntry + { + // Number of vertices in the geometry and index of the buffer + // This tells the surface manager which vertexbuffer this is in. + public int numvertices; + public int bufferindex; + + // Bounding box for fast culling + public RectangleF bbox; + + // Offset in the buffer (in number of vertices) + public int vertexoffset; + + // Sector geometry (local copy used to quickly refill buffers) + // The sector must set these! + public FlatVertex[] floorvertices; + public FlatVertex[] ceilvertices; + + // Sector images + // The sector must set these! + public long floortexture; + public long ceiltexture; + + // Constructor + internal SurfaceEntry(int numvertices, int bufferindex, int vertexoffset) + { + this.numvertices = numvertices; + this.bufferindex = bufferindex; + this.vertexoffset = vertexoffset; + } + + // Constructor that copies the entry, but does not copy the vertices + internal SurfaceEntry(SurfaceEntry oldentry) + { + this.numvertices = oldentry.numvertices; + this.bufferindex = oldentry.bufferindex; + this.vertexoffset = oldentry.vertexoffset; + } + + // This calculates the bounding box from the vertices + public void UpdateBBox() + { + float left = float.MaxValue; + float right = float.MinValue; + float top = float.MaxValue; + float bottom = float.MinValue; + + for (int i = 0; i < floorvertices.Length; i++) + { + if (floorvertices[i].x < left) left = floorvertices[i].x; + if (floorvertices[i].x > right) right = floorvertices[i].x; + if (floorvertices[i].y < top) top = floorvertices[i].y; + if (floorvertices[i].y > bottom) bottom = floorvertices[i].y; + } + + bbox = new RectangleF(left, top, right - left, bottom - top); + } + } +} diff --git a/Source/Core/Rendering/SurfaceManager.cs b/Source/Core/Rendering/SurfaceManager.cs new file mode 100644 index 0000000..c9c9a83 --- /dev/null +++ b/Source/Core/Rendering/SurfaceManager.cs @@ -0,0 +1,648 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using System.Drawing; +using System.ComponentModel; +using CodeImp.DoomBuilder.Map; +using SlimDX.Direct3D9; +using SlimDX; +using CodeImp.DoomBuilder.Geometry; +using System.Drawing.Imaging; +using CodeImp.DoomBuilder.Data; +using CodeImp.DoomBuilder.Editing; + +using Configuration = CodeImp.DoomBuilder.IO.Configuration; + +#endregion + +namespace CodeImp.DoomBuilder.Rendering +{ + internal class SurfaceManager : ID3DResource + { + #region ================== Constants + + // The true maximum lies at 65535 if I remember correctly, but that + // is a scary big number for a vertexbuffer. + private const int MAX_VERTICES_PER_BUFFER = 30000; + + #endregion + + #region ================== Variables + + // Set of buffers for a specific number of vertices per sector + private Dictionary sets; + + // List of buffers that are locked + // This is null when not in the process of updating + private List lockedbuffers; + + // Surface to be rendered. + // Each BinaryHeap in the Dictionary contains all geometry that needs + // to be rendered with the associated ImageData. + // The BinaryHeap sorts the geometry by sector to minimize stream switchs. + // This is null when not in the process of rendering + private Dictionary> surfaces; + + // This is 1 to add the number of vertices to the offset + // (effectively rendering the ceiling vertices instead of floor vertices) + private int surfacevertexoffsetmul; + + // This is set to true when the resources have been unloaded + private bool resourcesunloaded; + + #endregion + + #region ================== Properties + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + public SurfaceManager() + { + sets = new Dictionary(); + lockedbuffers = new List(); + + General.Map.Graphics.RegisterResource(this); + } + + // Disposer + public void Dispose() + { + if (sets != null) + { + General.Map.Graphics.UnregisterResource(this); + + // Dispose all sets + foreach (KeyValuePair set in sets) + { + // Dispose vertex buffers + for (int i = 0; i < set.Value.buffers.Count; i++) + { + if (set.Value.buffers[i] != null) + { + set.Value.buffers[i].Dispose(); + set.Value.buffers[i] = null; + } + } + } + + sets = null; + } + } + + #endregion + + #region ================== Management + + // Called when all resource must be unloaded + public void UnloadResource() + { + resourcesunloaded = true; + foreach (KeyValuePair set in sets) + { + // Dispose vertex buffers + for (int i = 0; i < set.Value.buffers.Count; i++) + { + if (set.Value.buffers[i] != null) + { + set.Value.buffers[i].Dispose(); + set.Value.buffers[i] = null; + } + } + } + + lockedbuffers.Clear(); + } + + // Called when all resource must be reloaded + public void ReloadResource() + { + foreach (KeyValuePair set in sets) + { + // Rebuild vertex buffers + for (int i = 0; i < set.Value.buffersizes.Count; i++) + { + // Make the new buffer! + VertexBuffer b = new VertexBuffer(General.Map.Graphics.Device, FlatVertex.Stride * set.Value.buffersizes[i], + Usage.WriteOnly | Usage.Dynamic, VertexFormat.None, Pool.Default); + + // Start refilling the buffer with sector geometry + int vertexoffset = 0; + DataStream bstream = b.Lock(0, FlatVertex.Stride * set.Value.buffersizes[i], LockFlags.Discard); + foreach (SurfaceEntry e in set.Value.entries) + { + if (e.bufferindex == i) + { + // Fill buffer + bstream.Seek(e.vertexoffset * FlatVertex.Stride, SeekOrigin.Begin); + bstream.WriteRange(e.floorvertices); + bstream.WriteRange(e.ceilvertices); + } + } + + // Unlock buffer + b.Unlock(); + bstream.Dispose(); + + // Add to list + set.Value.buffers[i] = b; + } + } + + resourcesunloaded = false; + } + + // This resets all buffers and requires all sectors to get new entries + public void Reset() + { + // Clear all items + foreach (KeyValuePair set in sets) + { + foreach (SurfaceEntry entry in set.Value.entries) + { + entry.numvertices = -1; + entry.bufferindex = -1; + } + + foreach (SurfaceEntry entry in set.Value.holes) + { + entry.numvertices = -1; + entry.bufferindex = -1; + } + + foreach (VertexBuffer vb in set.Value.buffers) + vb.Dispose(); + } + + // New dictionary + sets = new Dictionary(); + } + + // Updating sector surface geometry should go in this order; + // - Triangulate sectors + // - Call FreeSurfaces to remove entries that have changed number of vertices + // - Call AllocateBuffers + // - Call UpdateSurfaces to add/update entries + // - Call UnlockBuffers + + // This (re)allocates the buffers based on an analysis of the map + // The map must be updated (triangulated) before calling this + public void AllocateBuffers() + { + // Make analysis of sector geometry + Dictionary sectorverts = new Dictionary(); + foreach (Sector s in General.Map.Map.Sectors) + { + if (s.Triangles != null) + { + // We count the number of sectors that have specific number of vertices + if (!sectorverts.ContainsKey(s.Triangles.Vertices.Count)) + sectorverts.Add(s.Triangles.Vertices.Count, 0); + sectorverts[s.Triangles.Vertices.Count]++; + } + } + + // Now (re)allocate the needed buffers + foreach (KeyValuePair sv in sectorverts) + { + // Zero vertices can't be drawn + if (sv.Key > 0) + { + SurfaceBufferSet set = GetSet(sv.Key); + + // Calculte how many free entries we need + int neededentries = sv.Value; + int freeentriesneeded = neededentries - set.entries.Count; + + // Allocate the space needed + EnsureFreeBufferSpace(set, freeentriesneeded); + } + } + } + + // This ensures there is enough space for a given number of free entries (also adds new bufers if needed) + private void EnsureFreeBufferSpace(SurfaceBufferSet set, int freeentries) + { + DataStream bstream = null; + VertexBuffer vb = null; + + // Check if we have to add entries + int addentries = freeentries - set.holes.Count; + + // Begin resizing buffers starting with the last in this set + int bufferindex = set.buffers.Count - 1; + + // Calculate the maximum number of entries we can put in a new buffer + // Note that verticesperentry is the number of vertices multiplied by 2, because + // we have to store both the floor and ceiling + int verticesperentry = set.numvertices * 2; + int maxentriesperbuffer = MAX_VERTICES_PER_BUFFER / verticesperentry; + + // Make a new bufer when the last one is full + if ((bufferindex > -1) && (set.buffersizes[bufferindex] >= (maxentriesperbuffer * verticesperentry))) + bufferindex = -1; + + while (addentries > 0) + { + // Create a new buffer? + if ((bufferindex == -1) || (bufferindex > (set.buffers.Count - 1))) + { + // Determine the number of entries we will be making this buffer for + int bufferentries = (addentries > maxentriesperbuffer) ? maxentriesperbuffer : addentries; + + // Calculate the number of vertices that will be + int buffernumvertices = bufferentries * verticesperentry; + + if (!resourcesunloaded) + { + // Make the new buffer! + vb = new VertexBuffer(General.Map.Graphics.Device, FlatVertex.Stride * buffernumvertices, + Usage.WriteOnly | Usage.Dynamic, VertexFormat.None, Pool.Default); + + // Add it. + set.buffers.Add(vb); + } + else + { + // We can't make a vertexbuffer right now + set.buffers.Add(null); + } + + // Also add available entries as holes, because they are not used yet. + set.buffersizes.Add(buffernumvertices); + for (int i = 0; i < bufferentries; i++) + set.holes.Add(new SurfaceEntry(set.numvertices, set.buffers.Count - 1, i * verticesperentry)); + + // Done + addentries -= bufferentries; + } + // Reallocate a buffer + else + { + // Trash the old buffer + if (set.buffers[bufferindex].Tag != null) + { + bstream = (DataStream)set.buffers[bufferindex].Tag; + set.buffers[bufferindex].Unlock(); + bstream.Dispose(); + set.buffers[bufferindex].Tag = null; + } + + if ((set.buffers[bufferindex] != null) && !resourcesunloaded) + set.buffers[bufferindex].Dispose(); + + // Get the entries that are in this buffer only + List theseentries = new List(); + foreach (SurfaceEntry e in set.entries) + { + if (e.bufferindex == bufferindex) + theseentries.Add(e); + } + + // Determine the number of entries we will be making this buffer for + int bufferentries = ((theseentries.Count + addentries) > maxentriesperbuffer) ? maxentriesperbuffer : (theseentries.Count + addentries); + + // Calculate the number of vertices that will be + int buffernumvertices = bufferentries * verticesperentry; + + if (!resourcesunloaded) + { + // Make the new buffer and lock it + vb = new VertexBuffer(General.Map.Graphics.Device, FlatVertex.Stride * buffernumvertices, + Usage.WriteOnly | Usage.Dynamic, VertexFormat.None, Pool.Default); + bstream = vb.Lock(0, FlatVertex.Stride * theseentries.Count * verticesperentry, LockFlags.Discard); + } + + // Start refilling the buffer with sector geometry + int vertexoffset = 0; + foreach (SurfaceEntry e in theseentries) + { + if (!resourcesunloaded) + { + // Fill buffer + bstream.WriteRange(e.floorvertices); + bstream.WriteRange(e.ceilvertices); + } + + // Set the new location in the buffer + e.vertexoffset = vertexoffset; + + // Move on + vertexoffset += verticesperentry; + } + + if (!resourcesunloaded) + { + // Unlock buffer + vb.Unlock(); + bstream.Dispose(); + set.buffers[bufferindex] = vb; + } + else + { + // No vertex buffer at this time, sorry + set.buffers[bufferindex] = null; + } + + // Set the new buffer and add available entries as holes, because they are not used yet. + set.buffersizes[bufferindex] = buffernumvertices; + set.holes.Clear(); + for (int i = 0; i < bufferentries - theseentries.Count; i++) + set.holes.Add(new SurfaceEntry(set.numvertices, bufferindex, i * verticesperentry + vertexoffset)); + + // Done + addentries -= bufferentries; + } + + // Always continue in next (new) buffer + bufferindex = set.buffers.Count; + } + } + + // This adds or updates sector geometry into a buffer. + // Always specify the entry when a previous entry was already given for that sector! + // Sector must set the floorvertices and ceilvertices members on the entry. + // Returns the new surface entry for the stored geometry, floorvertices and ceilvertices will be preserved. + public SurfaceEntry UpdateSurfaces(SurfaceEntry entry) + { + if (entry.floorvertices.Length != entry.ceilvertices.Length) + General.Fail("Floor vertices has different length from ceiling vertices!"); + + int numvertices = entry.floorvertices.Length; + + // Free entry when number of vertices have changed + if ((entry.numvertices != numvertices) && (entry.numvertices != -1)) + FreeSurfaces(entry); + + // Check if we can render this at all + if (numvertices > 0) + { + SurfaceBufferSet set = GetSet(numvertices); + + // Update bounding box + entry.UpdateBBox(); + + // Check if we need a new entry + if (entry.numvertices == -1) + { + EnsureFreeBufferSpace(set, 1); + SurfaceEntry nentry = set.holes[set.holes.Count - 1]; + set.holes.RemoveAt(set.holes.Count - 1); + nentry.ceilvertices = entry.ceilvertices; + nentry.floorvertices = entry.floorvertices; + nentry.floortexture = entry.floortexture; + nentry.ceiltexture = entry.ceiltexture; + nentry.bbox = entry.bbox; + set.entries.Add(nentry); + entry = nentry; + } + + if (!resourcesunloaded) + { + // Lock the buffer + DataStream bstream; + VertexBuffer vb = set.buffers[entry.bufferindex]; + if (vb.Tag == null) + { + // Note: DirectX warns me that I am not using LockFlags.Discard or LockFlags.NoOverwrite here, + // but we don't care (we don't have much of a choice since we want to update our data) + bstream = vb.Lock(0, set.buffersizes[entry.bufferindex] * FlatVertex.Stride, LockFlags.None); + vb.Tag = bstream; + lockedbuffers.Add(vb); + } + else + { + bstream = (DataStream)vb.Tag; + } + + // Write the vertices to buffer + bstream.Seek(entry.vertexoffset * FlatVertex.Stride, SeekOrigin.Begin); + bstream.WriteRange(entry.floorvertices); + bstream.WriteRange(entry.ceilvertices); + } + } + + return entry; + } + + // This frees the given surface entry + public void FreeSurfaces(SurfaceEntry entry) + { + if ((entry.numvertices > 0) && (entry.bufferindex > -1)) + { + SurfaceBufferSet set = sets[entry.numvertices]; + set.entries.Remove(entry); + SurfaceEntry newentry = new SurfaceEntry(entry); + set.holes.Add(newentry); + } + entry.numvertices = -1; + entry.bufferindex = -1; + } + + // This unlocks the locked buffers + public void UnlockBuffers() + { + if (!resourcesunloaded) + { + foreach (VertexBuffer vb in lockedbuffers) + { + if (vb.Tag != null) + { + DataStream bstream = (DataStream)vb.Tag; + vb.Unlock(); + bstream.Dispose(); + vb.Tag = null; + } + } + + // Clear list + lockedbuffers = new List(); + } + } + + // This gets or creates a set for a specific number of vertices + private SurfaceBufferSet GetSet(int numvertices) + { + SurfaceBufferSet set; + + // Get or create the set + if (!sets.ContainsKey(numvertices)) + { + set = new SurfaceBufferSet(); + set.numvertices = numvertices; + set.buffers = new List(); + set.buffersizes = new List(); + set.entries = new List(); + set.holes = new List(); + sets.Add(numvertices, set); + } + else + { + set = sets[numvertices]; + } + + return set; + } + + #endregion + + #region ================== Rendering + + // This renders all sector floors + internal void RenderSectorFloors(RectangleF viewport) + { + surfaces = new Dictionary>(); + surfacevertexoffsetmul = 0; + + // Go for all surfaces as they are sorted in the buffers, so that + // they are automatically already sorted by vertexbuffer + foreach (KeyValuePair set in sets) + { + foreach (SurfaceEntry entry in set.Value.entries) + { + if (entry.bbox.IntersectsWith(viewport)) + AddSurfaceEntryForRendering(entry, entry.floortexture); + } + } + } + + // This renders all sector ceilings + internal void RenderSectorCeilings(RectangleF viewport) + { + surfaces = new Dictionary>(); + surfacevertexoffsetmul = 1; + + // Go for all surfaces as they are sorted in the buffers, so that + // they are automatically already sorted by vertexbuffer + foreach (KeyValuePair set in sets) + { + foreach (SurfaceEntry entry in set.Value.entries) + { + if (entry.bbox.IntersectsWith(viewport)) + AddSurfaceEntryForRendering(entry, entry.ceiltexture); + } + } + } + + // This renders all sector brightness levels + internal void RenderSectorBrightness(RectangleF viewport) + { + surfaces = new Dictionary>(); + surfacevertexoffsetmul = 0; + + // Go for all surfaces as they are sorted in the buffers, so that + // they are automatically already sorted by vertexbuffer + foreach (KeyValuePair set in sets) + { + foreach (SurfaceEntry entry in set.Value.entries) + { + if (entry.bbox.IntersectsWith(viewport)) + AddSurfaceEntryForRendering(entry, 0); + } + } + } + + // This adds a surface entry to the list of surfaces + private void AddSurfaceEntryForRendering(SurfaceEntry entry, long longimagename) + { + // Determine texture to use + ImageData img; + if (longimagename == 0) + { + img = General.Map.Data.WhiteTexture; + } + else + { + if (General.Map.Data.GetFlatExists(longimagename)) + { + img = General.Map.Data.GetFlatImageKnown(longimagename); + + // Is the texture loaded? + if (img.IsImageLoaded && !img.LoadFailed) + { + if (img.Texture == null) img.CreateTexture(); + } + else + { + img = General.Map.Data.WhiteTexture; + } + } + else + { + img = General.Map.Data.UnknownTexture3D; + } + } + + // Store by texture + if (!surfaces.ContainsKey(img)) + surfaces.Add(img, new List()); + surfaces[img].Add(entry); + } + + // This renders the sorted sector surfaces + internal void RenderSectorSurfaces(D3DDevice graphics) + { + int counter = 0; + if (!resourcesunloaded) + { + graphics.Shaders.Display2D.Begin(); + foreach (KeyValuePair> imgsurfaces in surfaces) + { + // Set texture + graphics.Shaders.Display2D.Texture1 = imgsurfaces.Key.Texture; + if (!graphics.Shaders.Enabled) graphics.Device.SetTexture(0, imgsurfaces.Key.Texture); + + graphics.Shaders.Display2D.BeginPass(1); + + // Go for all surfaces + VertexBuffer lastbuffer = null; + foreach (SurfaceEntry entry in imgsurfaces.Value) + { + // Set the vertex buffer + SurfaceBufferSet set = sets[entry.numvertices]; + if (set.buffers[entry.bufferindex] != lastbuffer) + { + lastbuffer = set.buffers[entry.bufferindex]; + graphics.Device.SetStreamSource(0, lastbuffer, 0, FlatVertex.Stride); + } + + // Draw + counter++; + graphics.Device.DrawPrimitives(PrimitiveType.TriangleList, entry.vertexoffset + (entry.numvertices * surfacevertexoffsetmul), entry.numvertices / 3); + } + + graphics.Shaders.Display2D.EndPass(); + } + graphics.Shaders.Display2D.End(); + } + Console.WriteLine("Calls: " + counter); + } + + #endregion + } +} diff --git a/Source/Core/Rendering/TextAlignment.cs b/Source/Core/Rendering/TextAlignment.cs new file mode 100644 index 0000000..fc567fa --- /dev/null +++ b/Source/Core/Rendering/TextAlignment.cs @@ -0,0 +1,56 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using System.Drawing; +using System.ComponentModel; +using CodeImp.DoomBuilder.Map; +using SlimDX.Direct3D9; +using SlimDX; +using CodeImp.DoomBuilder.Geometry; +using System.Drawing.Imaging; +using CodeImp.DoomBuilder.Data; +using CodeImp.DoomBuilder.Editing; + +#endregion + +namespace CodeImp.DoomBuilder.Rendering +{ + // This enumeration defines horizontal alignment + public enum TextAlignmentX : int + { + Left = 0, + Center = 1, + Right = 2 + } + + // This enumeration defines vertical alignment + public enum TextAlignmentY : int + { + Top = 0, + Middle = 1, + Bottom = 2 + } +} diff --git a/Source/Core/Rendering/TextFont.cs b/Source/Core/Rendering/TextFont.cs new file mode 100644 index 0000000..1af5403 --- /dev/null +++ b/Source/Core/Rendering/TextFont.cs @@ -0,0 +1,266 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using System.Drawing; +using System.ComponentModel; +using CodeImp.DoomBuilder.Map; +using SlimDX.Direct3D9; +using SlimDX; +using CodeImp.DoomBuilder.Geometry; +using System.Drawing.Imaging; +using CodeImp.DoomBuilder.Data; +using CodeImp.DoomBuilder.Editing; + +using Configuration = CodeImp.DoomBuilder.IO.Configuration; + +#endregion + +namespace CodeImp.DoomBuilder.Rendering +{ + internal class TextFont : IDisposable + { + #region ================== Structures + + // This structure defines character properties + private struct FontCharacter + { + // Variables + public float width; + public float height; + public float u1; + public float v1; + public float u2; + public float v2; + } + + #endregion + + #region ================== Constants + + // Font resource name + private const string FONT_RESOURCE = "Font.cfg"; + + // Spacing adjustments + private const float ADJUST_SPACING = -0.08f; + + #endregion + + #region ================== Variables + + // Characters + private FontCharacter[] characters; + + // Disposing + private bool isdisposed = false; + + #endregion + + #region ================== Properties + + public bool IsDisposed { get { return isdisposed; } } + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + internal TextFont() + { + Configuration cfg; + Stream fontdata; + StreamReader fontreader; + string[] resnames; + + // Initialize + characters = new FontCharacter[256]; + + // Make chars configuration + cfg = new Configuration(); + + // Find a resource named Font.cfg + resnames = General.ThisAssembly.GetManifestResourceNames(); + foreach (string rn in resnames) + { + // Found it? + if (rn.EndsWith(FONT_RESOURCE, StringComparison.InvariantCultureIgnoreCase)) + { + // Get a stream from the resource + fontdata = General.ThisAssembly.GetManifestResourceStream(rn); + fontreader = new StreamReader(fontdata, Encoding.ASCII); + + // Load configuration from stream + cfg.InputConfiguration(fontreader.ReadToEnd()); + + // Done + fontreader.Dispose(); + fontdata.Dispose(); + break; + } + } + + // Get the charset from configuration + IDictionary cfgchars = cfg.ReadSetting("chars", new Hashtable()); + + // Go for all defined chars + foreach (DictionaryEntry item in cfgchars) + { + // Get the character Hashtable + IDictionary chr = (IDictionary)item.Value; + int i = Convert.ToInt32(item.Key); + + // This is ancient code of mine. + // The charater sizes were based on 800x600 resolution. + characters[i].width = (float)(int)chr["width"] / 40f; + characters[i].height = (float)(int)chr["height"] / 30f; + characters[i].u1 = (float)chr["u1"]; + characters[i].v1 = (float)chr["v1"]; + characters[i].u2 = (float)chr["u2"]; + characters[i].v2 = (float)chr["v2"]; + } + + // We have no destructor + GC.SuppressFinalize(this); + } + + // Diposer + public void Dispose() + { + // Not already disposed? + if (!isdisposed) + { + // Done + isdisposed = true; + } + } + + #endregion + + #region ================== Methods + + // This sets up vertices for a specific character + // also advances vertsoffset and textx + public void SetupVertices(DataStream stream, byte c, float scale, int color, + ref float textx, float texty, float textheight, float offsetv) + { + FlatVertex vert = new FlatVertex(); + FontCharacter cinfo; + float cwidth; + + // Get the character information + cinfo = characters[c]; + cwidth = cinfo.width * scale; + + // Create lefttop vertex + vert.c = color; + vert.u = cinfo.u1; + vert.v = cinfo.v1 * 0.5f + offsetv; + vert.x = textx; + vert.y = texty; + stream.Write(vert); + + // Create leftbottom vertex + vert.c = color; + vert.u = cinfo.u1; + vert.v = cinfo.v2 * 0.5f + offsetv; + vert.x = textx; + vert.y = texty + textheight; + stream.Write(vert); + + // Create righttop vertex + vert.c = color; + vert.u = cinfo.u2; + vert.v = cinfo.v1 * 0.5f + offsetv; + vert.x = textx + cwidth; + vert.y = texty; + stream.Write(vert); + + // Create leftbottom vertex + vert.c = color; + vert.u = cinfo.u1; + vert.v = cinfo.v2 * 0.5f + offsetv; + vert.x = textx; + vert.y = texty + textheight; + stream.Write(vert); + + // Create righttop vertex + vert.c = color; + vert.u = cinfo.u2; + vert.v = cinfo.v1 * 0.5f + offsetv; + vert.x = textx + cwidth; + vert.y = texty; + stream.Write(vert); + + // Create rightbottom vertex + vert.c = color; + vert.u = cinfo.u2; + vert.v = cinfo.v2 * 0.5f + offsetv; + vert.x = textx + cwidth; + vert.y = texty + textheight; + stream.Write(vert); + + textx += (cwidth + (ADJUST_SPACING * scale)); + } + + // This checks if the given character exists in the charset + public bool Contains(char c) + { + // Convert character to ASCII + byte[] keybyte = Encoding.ASCII.GetBytes(c.ToString()); + return Contains(keybyte[0]); + } + + // This checks if the given character exists in the charset + public bool Contains(byte b) + { + // Check if the character has been set + return ((characters[b].width > 0.000000001f) || + (characters[b].height > 0.000000001f)); + } + + // This calculates the size of a text string at a given scale + public SizeF GetTextSize(string text, float scale) + { + // Size items + float sizex = 0, sizey = 0; + + // Get the ASCII bytes for the text + byte[] btext = Encoding.ASCII.GetBytes(text); + + // Go for all chars in text to calculate final text size + foreach (byte b in btext) + { + // Add to the final size + sizex += characters[b].width * scale; + sizey = characters[b].height * scale; + } + + // Return size + return new SizeF(sizex, sizey); + } + + #endregion + } +} diff --git a/Source/Core/Rendering/TextLabel.cs b/Source/Core/Rendering/TextLabel.cs new file mode 100644 index 0000000..257a801 --- /dev/null +++ b/Source/Core/Rendering/TextLabel.cs @@ -0,0 +1,283 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using System.Drawing; +using System.ComponentModel; +using CodeImp.DoomBuilder.Map; +using SlimDX.Direct3D9; +using SlimDX; +using CodeImp.DoomBuilder.Geometry; +using System.Drawing.Imaging; +using CodeImp.DoomBuilder.Data; +using CodeImp.DoomBuilder.Editing; + +#endregion + +namespace CodeImp.DoomBuilder.Rendering +{ + public class TextLabel : IDisposable, ID3DResource + { + #region ================== Constants + + #endregion + + #region ================== Variables + + // The text is stored as a polygon in a vertex buffer + private VertexBuffer textbuffer; + private int numfaces; + private int capacity; + + // Text settings + private string text; + private RectangleF rect; + private bool transformcoords; + private PixelColor color; + private PixelColor backcolor; + private float scale; + private TextAlignmentX alignx; + private TextAlignmentY aligny; + private SizeF size; + + // This keeps track if changes were made + private bool updateneeded; + private float lasttranslatex = float.MinValue; + private float lasttranslatey; + private float lastscalex; + private float lastscaley; + + // Disposing + private bool isdisposed = false; + + #endregion + + #region ================== Properties + + // Properties + public RectangleF Rectangle { get { return rect; } set { rect = value; updateneeded = true; } } + public float Left { get { return rect.X; } set { rect.X = value; updateneeded = true; } } + public float Top { get { return rect.Y; } set { rect.Y = value; updateneeded = true; } } + public float Width { get { return rect.Width; } set { rect.Width = value; updateneeded = true; } } + public float Height { get { return rect.Height; } set { rect.Height = value; updateneeded = true; } } + public float Right { get { return rect.Right; } set { rect.Width = value - rect.X + 1f; updateneeded = true; } } + public float Bottom { get { return rect.Bottom; } set { rect.Height = value - rect.Y + 1f; updateneeded = true; } } + public string Text { get { return text; } set { if (text != value.ToUpperInvariant()) { text = value.ToUpperInvariant(); updateneeded = true; } } } + public bool TransformCoords { get { return transformcoords; } set { transformcoords = value; updateneeded = true; } } + public SizeF TextSize { get { return size; } } + public float Scale { get { return scale; } set { scale = value; updateneeded = true; } } + public TextAlignmentX AlignX { get { return alignx; } set { alignx = value; updateneeded = true; } } + public TextAlignmentY AlignY { get { return aligny; } set { aligny = value; updateneeded = true; } } + public PixelColor Color { get { return color; } set { color = value; updateneeded = true; } } + public PixelColor Backcolor { get { return backcolor; } set { backcolor = value; updateneeded = true; } } + internal VertexBuffer VertexBuffer { get { return textbuffer; } } + internal int NumFaces { get { return numfaces; } } + + // Disposing + public bool IsDisposed { get { return isdisposed; } } + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + public TextLabel(int capacity) + { + // Initialize + this.text = ""; + this.rect = new RectangleF(0f, 0f, 1f, 1f); + this.color = new PixelColor(255, 255, 255, 255); + this.backcolor = new PixelColor(0, 0, 0, 0); + this.scale = 10f; + this.alignx = TextAlignmentX.Left; + this.aligny = TextAlignmentY.Top; + this.size = new SizeF(0f, 0f); + this.updateneeded = true; + this.numfaces = 0; + this.capacity = capacity; + + // Register as resource + General.Map.Graphics.RegisterResource(this); + + // We have no destructor + GC.SuppressFinalize(this); + } + + // Diposer + public void Dispose() + { + // Not already disposed? + if (!isdisposed) + { + // Clean up + UnloadResource(); + + // Unregister resource + General.Map.Graphics.UnregisterResource(this); + + // Done + isdisposed = true; + } + } + + #endregion + + #region ================== Methods + + // This updates the text if needed + internal void Update(float translatex, float translatey, float scalex, float scaley) + { + FlatVertex[] verts; + RectangleF absview; + float beginx = 0; + float beginy = 0; + bool colorcode = false; + int characters = 0; + byte[] textbytes; + DataStream stream; + + // Check if transformation changed and needs to be updated + if (transformcoords) + { + if ((translatex != lasttranslatex) || + (translatey != lasttranslatey) || + (scalex != lastscalex) || + (scaley != lastscaley)) updateneeded = true; + } + + // Update if needed + if (updateneeded) + { + // Only build when there are any vertices + if (text.Length > 0) + { + // Do we have to make a new buffer? + if ((textbuffer == null) || (text.Length > capacity)) + { + // Dispose previous + if (textbuffer != null) textbuffer.Dispose(); + + // Determine new capacity + if (capacity < text.Length) capacity = text.Length; + + // Create the buffer + textbuffer = new VertexBuffer(General.Map.Graphics.Device, + capacity * 12 * FlatVertex.Stride, + Usage.Dynamic | Usage.WriteOnly, + VertexFormat.None, Pool.Default); + } + + // Transform? + if (transformcoords) + { + // Calculate absolute coordinates + Vector2D lt = new Vector2D(rect.Left, rect.Top); + Vector2D rb = new Vector2D(rect.Right, rect.Bottom); + lt = lt.GetTransformed(translatex, translatey, scalex, scaley); + rb = rb.GetTransformed(translatex, translatey, scalex, scaley); + absview = new RectangleF(lt.x, lt.y, rb.x - lt.x, rb.y - lt.y); + } + else + { + // Fixed coordinates + absview = rect; + } + + // Calculate text dimensions + size = General.Map.Graphics.Font.GetTextSize(text, scale); + + // Align the text horizontally + switch (alignx) + { + case TextAlignmentX.Left: beginx = absview.X; break; + case TextAlignmentX.Center: beginx = absview.X + (absview.Width - size.Width) * 0.5f; break; + case TextAlignmentX.Right: beginx = absview.X + absview.Width - size.Width; break; + } + + // Align the text vertically + switch (aligny) + { + case TextAlignmentY.Top: beginy = absview.Y; break; + case TextAlignmentY.Middle: beginy = absview.Y + (absview.Height - size.Height) * 0.5f; break; + case TextAlignmentY.Bottom: beginy = absview.Y + absview.Height - size.Height; break; + } + + // Get the ASCII bytes for the text + textbytes = Encoding.ASCII.GetBytes(text); + + // Lock the buffer + stream = textbuffer.Lock(0, capacity * 12 * FlatVertex.Stride, + LockFlags.Discard | LockFlags.NoSystemLock); + + // Go for all chars in text to create the backgrounds + float textx = beginx; + foreach (byte b in textbytes) + General.Map.Graphics.Font.SetupVertices(stream, b, scale, backcolor.ToInt(), + ref textx, beginy, size.Height, 0.5f); + + // Go for all chars in text to create the text + textx = beginx; + foreach (byte b in textbytes) + General.Map.Graphics.Font.SetupVertices(stream, b, scale, color.ToInt(), + ref textx, beginy, size.Height, 0.0f); + + // Done filling the vertex buffer + textbuffer.Unlock(); + stream.Dispose(); + + // Calculate number of triangles + numfaces = text.Length * 4; + } + else + { + // No faces in polygon + numfaces = 0; + size = new SizeF(0f, 0f); + } + + // Text updated + updateneeded = false; + } + } + + // This unloads the resources + public void UnloadResource() + { + // Clean up + if (textbuffer != null) textbuffer.Dispose(); + textbuffer = null; + + // Need to update before we can render + updateneeded = true; + } + + // This (re)loads the resources + public void ReloadResource() + { + } + + #endregion + } +} diff --git a/Source/Core/Rendering/Things2DShader.cs b/Source/Core/Rendering/Things2DShader.cs new file mode 100644 index 0000000..892af10 --- /dev/null +++ b/Source/Core/Rendering/Things2DShader.cs @@ -0,0 +1,183 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using System.Drawing; +using System.ComponentModel; +using CodeImp.DoomBuilder.Map; +using SlimDX.Direct3D9; +using SlimDX; +using CodeImp.DoomBuilder.Geometry; +using System.Drawing.Imaging; + +#endregion + +namespace CodeImp.DoomBuilder.Rendering +{ + internal sealed class Things2DShader : D3DShader + { + #region ================== Variables + + // Property handlers + private EffectHandle texture1; + private EffectHandle rendersettings; + private EffectHandle transformsettings; + + #endregion + + #region ================== Properties + + public Texture Texture1 { set { if (manager.Enabled) effect.SetTexture(texture1, value); } } + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + public Things2DShader(ShaderManager manager) : base(manager) + { + // Load effect from file + effect = LoadEffect("things2d.fx"); + + // Get the property handlers from effect + if (effect != null) + { + texture1 = effect.GetParameter(null, "texture1"); + rendersettings = effect.GetParameter(null, "rendersettings"); + transformsettings = effect.GetParameter(null, "transformsettings"); + } + + // Initialize world vertex declaration + VertexElement[] elements = new VertexElement[] + { + new VertexElement(0, 0, DeclarationType.Float3, DeclarationMethod.Default, DeclarationUsage.Position, 0), + new VertexElement(0, 12, DeclarationType.Color, DeclarationMethod.Default, DeclarationUsage.Color, 0), + new VertexElement(0, 16, DeclarationType.Float2, DeclarationMethod.Default, DeclarationUsage.TextureCoordinate, 0), + VertexElement.VertexDeclarationEnd + }; + vertexdecl = new VertexDeclaration(General.Map.Graphics.Device, elements); + + // We have no destructor + GC.SuppressFinalize(this); + } + + // Disposer + public override void Dispose() + { + // Not already disposed? + if (!isdisposed) + { + // Clean up + if (texture1 != null) texture1.Dispose(); + if (rendersettings != null) rendersettings.Dispose(); + if (transformsettings != null) transformsettings.Dispose(); + + // Done + base.Dispose(); + } + } + + #endregion + + #region ================== Methods + + // This sets the settings + public void SetSettings(float alpha) + { + if (manager.Enabled) + { + Vector4 values = new Vector4(0.0f, 0.0f, 1.0f, alpha); + effect.SetValue(rendersettings, values); + Matrix world = manager.D3DDevice.Device.GetTransform(TransformState.World); + Matrix view = manager.D3DDevice.Device.GetTransform(TransformState.View); + effect.SetValue(transformsettings, Matrix.Multiply(world, view)); + } + } + + // This sets up the render pipeline + public override void BeginPass(int index) + { + Device device = manager.D3DDevice.Device; + + if (!manager.Enabled) + { + // Sampler settings + if (General.Settings.ClassicBilinear) + { + device.SetSamplerState(0, SamplerState.MagFilter, TextureFilter.Linear); + device.SetSamplerState(0, SamplerState.MinFilter, TextureFilter.Linear); + device.SetSamplerState(0, SamplerState.MipFilter, TextureFilter.Linear); + device.SetSamplerState(0, SamplerState.MipMapLodBias, 0f); + } + else + { + device.SetSamplerState(0, SamplerState.MagFilter, TextureFilter.Point); + device.SetSamplerState(0, SamplerState.MinFilter, TextureFilter.Point); + device.SetSamplerState(0, SamplerState.MipFilter, TextureFilter.Point); + device.SetSamplerState(0, SamplerState.MipMapLodBias, 0f); + } + + // Texture addressing + device.SetSamplerState(0, SamplerState.AddressU, TextureAddress.Wrap); + device.SetSamplerState(0, SamplerState.AddressV, TextureAddress.Wrap); + device.SetSamplerState(0, SamplerState.AddressW, TextureAddress.Wrap); + + // First texture stage + device.SetTextureStageState(0, TextureStage.ColorOperation, TextureOperation.Modulate); + device.SetTextureStageState(0, TextureStage.ColorArg1, TextureArgument.Texture); + device.SetTextureStageState(0, TextureStage.ColorArg2, TextureArgument.Diffuse); + device.SetTextureStageState(0, TextureStage.ResultArg, TextureArgument.Current); + device.SetTextureStageState(0, TextureStage.TexCoordIndex, 0); + + // Second texture stage + device.SetTextureStageState(1, TextureStage.ColorOperation, TextureOperation.Modulate); + device.SetTextureStageState(1, TextureStage.ColorArg1, TextureArgument.Current); + device.SetTextureStageState(1, TextureStage.ColorArg2, TextureArgument.TFactor); + device.SetTextureStageState(1, TextureStage.ResultArg, TextureArgument.Current); + device.SetTextureStageState(1, TextureStage.TexCoordIndex, 0); + + // No more further stages + device.SetTextureStageState(2, TextureStage.ColorOperation, TextureOperation.Disable); + + // First alpha stage + device.SetTextureStageState(0, TextureStage.AlphaOperation, TextureOperation.Modulate); + device.SetTextureStageState(0, TextureStage.AlphaArg1, TextureArgument.Texture); + device.SetTextureStageState(0, TextureStage.AlphaArg2, TextureArgument.Diffuse); + + // Second alpha stage + device.SetTextureStageState(1, TextureStage.AlphaOperation, TextureOperation.Modulate); + device.SetTextureStageState(1, TextureStage.AlphaArg1, TextureArgument.Current); + device.SetTextureStageState(1, TextureStage.AlphaArg2, TextureArgument.TFactor); + + // No more further stages + device.SetTextureStageState(2, TextureStage.AlphaOperation, TextureOperation.Disable); + } + + base.BeginPass(index); + } + + #endregion + } +} diff --git a/Source/Core/Rendering/ViewMode.cs b/Source/Core/Rendering/ViewMode.cs new file mode 100644 index 0000000..be297a1 --- /dev/null +++ b/Source/Core/Rendering/ViewMode.cs @@ -0,0 +1,50 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using System.Drawing; +using System.ComponentModel; +using CodeImp.DoomBuilder.Map; +using SlimDX.Direct3D9; +using SlimDX; +using CodeImp.DoomBuilder.Geometry; +using System.Drawing.Imaging; +using CodeImp.DoomBuilder.Data; +using CodeImp.DoomBuilder.Editing; + +#endregion + +namespace CodeImp.DoomBuilder.Rendering +{ + public enum ViewMode : int + { + Normal = 0, + FloorTextures = 1, + CeilingTextures = 2, + FloorColor = 3, // villsa + CeilingColor = 4, // villsa + ThingColor = 5, // villsa + } +} diff --git a/Source/Core/Rendering/World3DShader.cs b/Source/Core/Rendering/World3DShader.cs new file mode 100644 index 0000000..54c4f07 --- /dev/null +++ b/Source/Core/Rendering/World3DShader.cs @@ -0,0 +1,260 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using System.Drawing; +using System.ComponentModel; +using CodeImp.DoomBuilder.Map; +using SlimDX.Direct3D9; +using SlimDX; +using CodeImp.DoomBuilder.Geometry; +using System.Drawing.Imaging; + +#endregion + +namespace CodeImp.DoomBuilder.Rendering +{ + internal sealed class World3DShader : D3DShader + { + #region ================== Variables + + // Property handlers + private EffectHandle texture1; + private EffectHandle worldviewproj; + private EffectHandle minfiltersettings; + private EffectHandle magfiltersettings; + private EffectHandle modulatecolor; + private EffectHandle highlightcolor; + + #endregion + + #region ================== Properties + + public Matrix WorldViewProj { set { if (manager.Enabled) effect.SetValue(worldviewproj, value); } } + public Texture Texture1 { set { if (manager.Enabled) effect.SetTexture(texture1, value); } } + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + public World3DShader(ShaderManager manager) : base(manager) + { + // Load effect from file + effect = LoadEffect("world3d.fx"); + + // Get the property handlers from effect + if (effect != null) + { + worldviewproj = effect.GetParameter(null, "worldviewproj"); + texture1 = effect.GetParameter(null, "texture1"); + minfiltersettings = effect.GetParameter(null, "minfiltersettings"); + magfiltersettings = effect.GetParameter(null, "magfiltersettings"); + modulatecolor = effect.GetParameter(null, "modulatecolor"); + highlightcolor = effect.GetParameter(null, "highlightcolor"); + } + + // Initialize world vertex declaration + VertexElement[] elements = new VertexElement[] + { + new VertexElement(0, 0, DeclarationType.Float3, DeclarationMethod.Default, DeclarationUsage.Position, 0), + new VertexElement(0, 12, DeclarationType.Color, DeclarationMethod.Default, DeclarationUsage.Color, 0), + new VertexElement(0, 16, DeclarationType.Float2, DeclarationMethod.Default, DeclarationUsage.TextureCoordinate, 0), + VertexElement.VertexDeclarationEnd + }; + vertexdecl = new VertexDeclaration(General.Map.Graphics.Device, elements); + + // We have no destructor + GC.SuppressFinalize(this); + } + + // Disposer + public override void Dispose() + { + // Not already disposed? + if (!isdisposed) + { + // Clean up + if (texture1 != null) texture1.Dispose(); + if (worldviewproj != null) worldviewproj.Dispose(); + if (minfiltersettings != null) minfiltersettings.Dispose(); + if (magfiltersettings != null) magfiltersettings.Dispose(); + if (modulatecolor != null) modulatecolor.Dispose(); + if (highlightcolor != null) highlightcolor.Dispose(); + + // Done + base.Dispose(); + } + } + + #endregion + + #region ================== Methods + + // This sets the constant settings + public void SetConstants(bool bilinear, bool useanisotropic) + { + if (manager.Enabled) + { + if (bilinear) + { + effect.SetValue(magfiltersettings, (int)TextureFilter.Linear); + if (useanisotropic) effect.SetValue(minfiltersettings, (int)TextureFilter.Anisotropic); + } + else + { + effect.SetValue(magfiltersettings, (int)TextureFilter.Point); + effect.SetValue(minfiltersettings, (int)TextureFilter.Point); + } + } + } + + // This sets the modulation color + public void SetModulateColor(int modcolor) + { + if (manager.Enabled) + { + effect.SetValue(modulatecolor, new Color4(modcolor)); + } + } + + // This sets the highlight color + public void SetHighlightColor(int hicolor) + { + if (manager.Enabled) + { + effect.SetValue(highlightcolor, new Color4(hicolor)); + } + } + + // This sets up the render pipeline + public override void BeginPass(int index) + { + Device device = manager.D3DDevice.Device; + + if (!manager.Enabled) + { + // Sampler settings + if (General.Settings.VisualBilinear) + { + device.SetSamplerState(0, SamplerState.MagFilter, TextureFilter.Linear); + device.SetSamplerState(0, SamplerState.MinFilter, TextureFilter.Linear); + device.SetSamplerState(0, SamplerState.MipFilter, TextureFilter.Linear); + device.SetSamplerState(0, SamplerState.MipMapLodBias, 0f); + } + else + { + device.SetSamplerState(0, SamplerState.MagFilter, TextureFilter.Point); + device.SetSamplerState(0, SamplerState.MinFilter, TextureFilter.Point); + device.SetSamplerState(0, SamplerState.MipFilter, TextureFilter.Point); + device.SetSamplerState(0, SamplerState.MipMapLodBias, 0f); + } + + // Texture addressing + device.SetSamplerState(0, SamplerState.AddressU, TextureAddress.Wrap); + device.SetSamplerState(0, SamplerState.AddressV, TextureAddress.Wrap); + device.SetSamplerState(0, SamplerState.AddressW, TextureAddress.Wrap); + + // First texture stage + if ((index == 0) || (index == 2)) + { + // Normal + device.SetTextureStageState(0, TextureStage.ColorOperation, TextureOperation.Modulate); + device.SetTextureStageState(0, TextureStage.ColorArg1, TextureArgument.Texture); + device.SetTextureStageState(0, TextureStage.ColorArg2, TextureArgument.Diffuse); + device.SetTextureStageState(0, TextureStage.ResultArg, TextureArgument.Current); + device.SetTextureStageState(0, TextureStage.TexCoordIndex, 0); + } + else + { + // Full brightness + device.SetTextureStageState(0, TextureStage.ColorOperation, TextureOperation.SelectArg1); + device.SetTextureStageState(0, TextureStage.ColorArg1, TextureArgument.Texture); + device.SetTextureStageState(0, TextureStage.ResultArg, TextureArgument.Current); + device.SetTextureStageState(0, TextureStage.TexCoordIndex, 0); + } + + // First alpha stage + device.SetTextureStageState(0, TextureStage.AlphaOperation, TextureOperation.Modulate); + device.SetTextureStageState(0, TextureStage.AlphaArg1, TextureArgument.Texture); + device.SetTextureStageState(0, TextureStage.AlphaArg2, TextureArgument.Diffuse); + + // Second texture stage + device.SetTextureStageState(1, TextureStage.ColorOperation, TextureOperation.Modulate); + device.SetTextureStageState(1, TextureStage.ColorArg1, TextureArgument.Current); + device.SetTextureStageState(1, TextureStage.ColorArg2, TextureArgument.TFactor); + device.SetTextureStageState(1, TextureStage.ResultArg, TextureArgument.Current); + device.SetTextureStageState(1, TextureStage.TexCoordIndex, 0); + + // Second alpha stage + device.SetTextureStageState(1, TextureStage.AlphaOperation, TextureOperation.Modulate); + device.SetTextureStageState(1, TextureStage.AlphaArg1, TextureArgument.Current); + device.SetTextureStageState(1, TextureStage.AlphaArg2, TextureArgument.TFactor); + + // Highlight? + if (index > 1) + { + // Third texture stage + device.SetTextureStageState(2, TextureStage.ColorOperation, TextureOperation.AddSigned); + device.SetTextureStageState(2, TextureStage.ColorArg1, TextureArgument.Current); + device.SetTextureStageState(2, TextureStage.ColorArg2, TextureArgument.Texture); + device.SetTextureStageState(2, TextureStage.ColorArg0, TextureArgument.Texture); + device.SetTextureStageState(2, TextureStage.ResultArg, TextureArgument.Current); + device.SetTextureStageState(2, TextureStage.TexCoordIndex, 0); + + // Third alpha stage + device.SetTextureStageState(2, TextureStage.AlphaOperation, TextureOperation.SelectArg1); + device.SetTextureStageState(2, TextureStage.AlphaArg1, TextureArgument.Current); + + // Fourth texture stage + device.SetTextureStageState(3, TextureStage.ColorOperation, TextureOperation.AddSigned); + device.SetTextureStageState(3, TextureStage.ColorArg1, TextureArgument.Current); + device.SetTextureStageState(3, TextureStage.ColorArg2, TextureArgument.Texture); + device.SetTextureStageState(3, TextureStage.ColorArg0, TextureArgument.Texture); + device.SetTextureStageState(3, TextureStage.ResultArg, TextureArgument.Current); + device.SetTextureStageState(3, TextureStage.TexCoordIndex, 0); + + // Fourth alpha stage + device.SetTextureStageState(3, TextureStage.AlphaOperation, TextureOperation.SelectArg1); + device.SetTextureStageState(3, TextureStage.AlphaArg1, TextureArgument.Current); + + // No more further stages + device.SetTextureStageState(4, TextureStage.ColorOperation, TextureOperation.Disable); + device.SetTextureStageState(4, TextureStage.AlphaOperation, TextureOperation.Disable); + } + else + { + // No more further stages + device.SetTextureStageState(2, TextureStage.ColorOperation, TextureOperation.Disable); + device.SetTextureStageState(2, TextureStage.AlphaOperation, TextureOperation.Disable); + } + } + + base.BeginPass(index); + } + + #endregion + } +} diff --git a/Source/Core/Rendering/WorldVertex.cs b/Source/Core/Rendering/WorldVertex.cs new file mode 100644 index 0000000..be89bbb --- /dev/null +++ b/Source/Core/Rendering/WorldVertex.cs @@ -0,0 +1,97 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using System.Drawing; +using System.ComponentModel; +using CodeImp.DoomBuilder.Map; +using SlimDX.Direct3D9; +using SlimDX; +using CodeImp.DoomBuilder.Geometry; +using System.Drawing.Imaging; + +#endregion + +namespace CodeImp.DoomBuilder.Rendering +{ + // WorldVertex + public struct WorldVertex + { + // Vertex format + public static readonly int Stride = 6 * 4; + + // Members + public float x; + public float y; + public float z; + public int c; + public float u; + public float v; + + // Constructor + public WorldVertex(float x, float y, float z, int c, float u, float v) + { + this.x = x; + this.y = y; + this.z = z; + this.c = c; + this.u = u; + this.v = v; + } + + // Constructor + public WorldVertex(float x, float y, float z, float u, float v) + { + this.x = x; + this.y = y; + this.z = z; + this.c = -1; + this.u = u; + this.v = v; + } + + // Constructor + public WorldVertex(float x, float y, float z, int c) + { + this.x = x; + this.y = y; + this.z = z; + this.c = c; + this.u = 0.0f; + this.v = 0.0f; + } + + // Constructor + public WorldVertex(float x, float y, float z) + { + this.x = x; + this.y = y; + this.z = z; + this.c = -1; + this.u = 0.0f; + this.v = 0.0f; + } + } +} diff --git a/Source/Core/Resources/Actions.cfg b/Source/Core/Resources/Actions.cfg new file mode 100644 index 0000000..6878574 --- /dev/null +++ b/Source/Core/Resources/Actions.cfg @@ -0,0 +1,876 @@ +/******************************************\ + Doom Builder Actions Configuration +\******************************************/ + +// Categories for the Controls preferences +categories +{ + file = "File"; + edit = "Edit"; + view = "View"; + modes = "Modes"; + tools = "Tools"; + prefabs = "Prefabs"; + classic = "Classic Modes"; + visual = "Visual Modes"; + selecting = "Selecting"; +} + +// This just defines which actions there are, what description they have and +// some behaviour options. The Doom Builder core will bind to these actions +// with delegates (function pointers) where you use the BeginAction and +// EndAction attributes. + +// Options: +// +// allowkeys: Allows the user to bind standard keys to this action. +// allowmouse: Allows the user to bind mouse buttons to this action. +// allowscroll: Allows the user to bind the scrollwheel to this action. +// disregardshift: This action will trigger regardless if Shift is used. +// disregardcontrol: This action will be triggered regardless if Control is used. +// repeat: BeginAction will be called for automatic key repetition. +// default: Default key is only used when the action is loaded for the first +// time and the default key is not used by any other action. +// +// allowkeys and allowmouse are true by default, the others are false by default. +// + +/* +testaction +{ + title = "Developer Test"; + category = "tools"; + description = "Does whatever the developer wants to test."; + allowkeys = true; + allowmouse = true; + allowscroll = true; +} +*/ + +newmap +{ + title = "New Map"; + category = "file"; + description = "Starts with a new, empty workspace to begin drawing a map from scratch."; + allowkeys = true; + allowmouse = false; + allowscroll = false; +} + +openmap +{ + title = "Open Map"; + category = "file"; + description = "Opens an existing map from WAD file for viewing or modifying."; + allowkeys = true; + allowmouse = false; + allowscroll = false; +} + +closemap +{ + title = "Close Map"; + category = "file"; + description = "Closes the current map and the WAD file in which it exists."; + allowkeys = true; + allowmouse = false; + allowscroll = false; +} + +savemap +{ + title = "Save Map"; + category = "file"; + description = "Saves the current map to the opened source WAD file."; + allowkeys = true; + allowmouse = false; + allowscroll = false; +} + +savemapas +{ + title = "Save Map As"; + category = "file"; + description = "Saves the current map and all resources from the source WAD file to a new WAD file."; + allowkeys = true; + allowmouse = false; + allowscroll = false; +} + +savemapinto +{ + title = "Save Map Into"; + category = "file"; + description = "Saves the current map without any other resources into an existing or new WAD file."; + allowkeys = true; + allowmouse = false; + allowscroll = false; +} + +mapoptions +{ + title = "Map Options"; + category = "edit"; + description = "Shows the Map Options dialog which allows changing the map lump name, game configuration and custom resources."; + allowkeys = true; + allowmouse = false; + allowscroll = false; +} + +configuration +{ + title = "Game Configurations"; + category = "tools"; + description = "Shows the Game Configurations dialog which allows you to configure settings such as nodebuilder, testing program and resources."; + allowkeys = true; + allowmouse = false; + allowscroll = false; +} + +preferences +{ + title = "Preferences"; + category = "tools"; + description = "Shows this Preferences dialog."; + allowkeys = true; + allowmouse = false; + allowscroll = false; +} + +reloadresources +{ + title = "Reload Resources"; + category = "tools"; + description = "Reloads all data resources such as game configuration, textures and flats. Useful when resource files have been changed outside of Doom Builder."; + allowkeys = true; + allowmouse = false; + allowscroll = false; +} + +cancelmode +{ + title = "Cancel Action"; + category = "classic"; + description = "Cancels the current action and switches back to normal editing mode."; + allowkeys = true; + allowmouse = true; + allowscroll = true; +} + +acceptmode +{ + title = "Accept Action"; + category = "classic"; + description = "Accepts the changes in the current action and switches back to normal editing mode."; + allowkeys = true; + allowmouse = true; + allowscroll = true; +} + +classicselect +{ + title = "Select"; + category = "classic"; + description = "Selects the highlighted item. Also allows selection by dragging a rectangle. Hold shift while dragging a rectangle to toggle additive or normal selection."; + allowkeys = true; + allowmouse = true; + allowscroll = false; + disregardshift = true; +} + +classicedit +{ + title = "Edit"; + category = "classic"; + description = "Edits the properties of the selected items or drags the selected items. Also initiates drawing or inserts new things when no selection is made. Can be used in combination with Control and Shift for the first drawn vertex."; + allowkeys = true; + allowmouse = true; + allowscroll = false; + disregardshift = true; + disregardcontrol = true; +} + +scrollwest +{ + title = "Scroll West"; + category = "classic"; + description = "Scrolls the 2D map view to the left."; + allowkeys = true; + allowmouse = true; + allowscroll = true; + repeat = true; +} + +scrolleast +{ + title = "Scroll East"; + category = "classic"; + description = "Scrolls the 2D map view to the right."; + allowkeys = true; + allowmouse = true; + allowscroll = true; + repeat = true; +} + +scrollnorth +{ + title = "Scroll North"; + category = "classic"; + description = "Scrolls the 2D map view up."; + allowkeys = true; + allowmouse = true; + allowscroll = true; + repeat = true; +} + +scrollsouth +{ + title = "Scroll South"; + category = "classic"; + description = "Scrolls the 2D map view down."; + allowkeys = true; + allowmouse = true; + allowscroll = true; + repeat = true; +} + +pan_view +{ + title = "Pan View"; + category = "classic"; + description = "Pans the map in the direction of the mouse while held down."; + allowkeys = true; + allowmouse = true; + allowscroll = false; +} + +zoomin +{ + title = "Zoom In"; + category = "classic"; + description = "Zooms in on the map at the current mouse location."; + allowkeys = true; + allowmouse = true; + allowscroll = true; + repeat = true; +} + +zoomout +{ + title = "Zoom Out"; + category = "classic"; + description = "Zooms out on the map from the current mouse location."; + allowkeys = true; + allowmouse = true; + allowscroll = true; + repeat = true; +} + +centerinscreen +{ + title = "Fit To Screen"; + category = "classic"; + description = "Zooms out the map and centers it to make it completely fit in the screen, giving a high overview of the map."; + allowkeys = true; + allowmouse = true; + allowscroll = true; +} + +insertitem +{ + title = "Insert Item"; + category = "edit"; + description = "Creates a new item depending on the editing mode you are in."; + allowkeys = true; + allowmouse = true; + allowscroll = true; +} + +deleteitem +{ + title = "Delete Item"; + category = "edit"; + description = "Deletes the highlighted or selected items, depending on the editing mode you are in."; + allowkeys = true; + allowmouse = true; + allowscroll = true; +} + +gridsetup +{ + title = "Grid Setup"; + category = "classic"; + description = "Shows the Custom Grid Setup dialog which allows you to set custom grid sizes and a background image."; + allowkeys = true; + allowmouse = true; + allowscroll = true; +} + +griddec // Note, these were incorrectly swapped before, hence the wrong action name +{ + title = "Grid Increase"; + category = "edit"; + description = "Increases the grid size, decreasing the grid density."; + unbind = 1; + mousebuttons = 0; + mousescroll = 1; +} + + +gridinc // Note, these were incorrectly swapped before, hence the wrong action name +{ + title = "Grid Decrease"; + category = "edit"; + description = "Decreases the grid size, increasing the grid density."; + unbind = 1; + mousebuttons = 0; + mousescroll = 1; +} + +undo +{ + title = "Undo"; + category = "edit"; + description = "Restores the current map as it was before last action(s) performed."; + allowkeys = true; + allowmouse = true; + allowscroll = true; +} + +redo +{ + title = "Redo"; + category = "edit"; + description = "Repeats the action(s) performed before Undo was used."; + allowkeys = true; + allowmouse = true; + allowscroll = true; +} + +togglesnap +{ + title = "Snap to Grid"; + category = "edit"; + description = "Toggles snapping to the grid for things and vertices that are being dragged."; + allowkeys = true; + allowmouse = false; + allowscroll = false; +} + +toggleautomerge +{ + title = "Merge Geometry"; + category = "edit"; + description = "Toggles automatic merging of geometry for vertices and structures that are being dragged."; + allowkeys = true; + allowmouse = true; + allowscroll = true; +} + +togglefullbrightness +{ + title = "Toggle Full Brightness"; + category = "view"; + description = "Toggles the use of sector brightness on and off. When sector brightness is off, the world is displayed fully bright, without lighting effects."; + allowkeys = true; + allowmouse = true; + allowscroll = true; + default = 66; //B +} + +clearselection +{ + title = "Clear Selection"; + category = "edit"; + description = "Deselects all selected elements."; + allowkeys = true; + allowmouse = true; + allowscroll = true; +} + +moveforward +{ + title = "Move Forward"; + category = "visual"; + description = "Moves the camera forward in 3D Visual Mode. Use in combination with Shift for double the speed."; + allowkeys = true; + allowmouse = true; + allowscroll = true; + disregardshift = true; +} + +movebackward +{ + title = "Move Backward"; + category = "visual"; + description = "Moves the camera backward in 3D Visual Mode. Use in combination with Shift for double the speed."; + allowkeys = true; + allowmouse = true; + allowscroll = true; + disregardshift = true; +} + +moveleft +{ + title = "Move Left (strafe)"; + category = "visual"; + description = "Strafes the camera left in 3D Visual Mode. Use in combination with Shift for double the speed."; + allowkeys = true; + allowmouse = true; + allowscroll = true; + disregardshift = true; +} + +moveright +{ + title = "Move Right (strafe)"; + category = "visual"; + description = "Strafes the camera right in 3D Visual Mode. Use in combination with Shift for double the speed."; + allowkeys = true; + allowmouse = true; + allowscroll = true; + disregardshift = true; +} + +moveup +{ + title = "Move Up"; + category = "visual"; + description = "Moves the camera up in 3D Visual Mode. Use in combination with Shift for double the speed."; + allowkeys = true; + allowmouse = true; + allowscroll = true; + disregardshift = true; +} + +movedown +{ + title = "Move Down"; + category = "visual"; + description = "Moves the camera down in 3D Visual Mode. Use in combination with Shift for double the speed."; + allowkeys = true; + allowmouse = true; + allowscroll = true; + disregardshift = true; +} + +testmap +{ + title = "Test Map"; + category = "tools"; + description = "Starts the game and loads this map for playing."; + allowkeys = true; + allowmouse = false; + allowscroll = false; +} + +thingsfilterssetup +{ + title = "Configure Things Filters"; + category = "tools"; + description = "Shows the Things Filters setup dialog which allows you to add, remove and change the things filters."; + allowkeys = true; + allowmouse = false; + allowscroll = false; +} + +thingrotateleft +{ + title = "Rotate Thing Left"; + category = "edit"; + description = "Rotate Selected Things (+45 Degrees)"; + allowkeys = true; + allowmouse = true; + allowscroll = true; +} + +thingrotateright +{ + title = "Rotate Thing Right"; + category = "edit"; + description = "Rotate Selected Things (-45 Degrees)"; + allowkeys = true; + allowmouse = true; + allowscroll = true; +} + +copyselection +{ + title = "Copy Selection"; + category = "edit"; + description = "Copies the current selection to the clipboard."; + allowkeys = true; + allowmouse = true; + allowscroll = false; +} + +cutselection +{ + title = "Cut Selection"; + category = "edit"; + description = "Copies the current selection to the clipboard and removes it from the map."; + allowkeys = true; + allowmouse = true; + allowscroll = false; +} + +pasteselection +{ + title = "Paste Selection"; + category = "edit"; + description = "Pastes the current contents of the clipboard into the map as a new selection."; + allowkeys = true; + allowmouse = true; + allowscroll = false; +} + +pasteselectionspecial +{ + title = "Paste Selection Special"; + category = "edit"; + description = "Allows you to choose options or pasting and then pastes the current contents of the clipboard into the map as a new selection."; + allowkeys = true; + allowmouse = true; + allowscroll = false; +} + +selectgroup1 +{ + title = "Select Group 1"; + category = "selecting"; + description = "Selects all geometry that was assigned to group 1"; + allowkeys = true; + allowmouse = true; + allowscroll = false; +} + +selectgroup2 +{ + title = "Select Group 2"; + category = "selecting"; + description = "Selects all geometry that was assigned to group 2"; + allowkeys = true; + allowmouse = true; + allowscroll = false; +} + +selectgroup3 +{ + title = "Select Group 3"; + category = "selecting"; + description = "Selects all geometry that was assigned to group 3"; + allowkeys = true; + allowmouse = true; + allowscroll = false; +} + +selectgroup4 +{ + title = "Select Group 4"; + category = "selecting"; + description = "Selects all geometry that was assigned to group 4"; + allowkeys = true; + allowmouse = true; + allowscroll = false; +} + +selectgroup5 +{ + title = "Select Group 5"; + category = "selecting"; + description = "Selects all geometry that was assigned to group 5"; + allowkeys = true; + allowmouse = true; + allowscroll = false; +} + +selectgroup6 +{ + title = "Select Group 6"; + category = "selecting"; + description = "Selects all geometry that was assigned to group 6"; + allowkeys = true; + allowmouse = true; + allowscroll = false; +} + +selectgroup7 +{ + title = "Select Group 7"; + category = "selecting"; + description = "Selects all geometry that was assigned to group 7"; + allowkeys = true; + allowmouse = true; + allowscroll = false; +} + +selectgroup8 +{ + title = "Select Group 8"; + category = "selecting"; + description = "Selects all geometry that was assigned to group 8"; + allowkeys = true; + allowmouse = true; + allowscroll = false; +} + +selectgroup9 +{ + title = "Select Group 9"; + category = "selecting"; + description = "Selects all geometry that was assigned to group 9"; + allowkeys = true; + allowmouse = true; + allowscroll = false; +} + +selectgroup10 +{ + title = "Select Group 10"; + category = "selecting"; + description = "Selects all geometry that was assigned to group 10"; + allowkeys = true; + allowmouse = true; + allowscroll = false; +} + +assigngroup1 +{ + title = "Assign Group 1"; + category = "selecting"; + description = "Assigns the selected geometry to group 1"; + allowkeys = true; + allowmouse = true; + allowscroll = false; +} + +assigngroup2 +{ + title = "Assign Group 2"; + category = "selecting"; + description = "Assigns the selected geometry to group 2"; + allowkeys = true; + allowmouse = true; + allowscroll = false; +} + +assigngroup3 +{ + title = "Assign Group 3"; + category = "selecting"; + description = "Assigns the selected geometry to group 3"; + allowkeys = true; + allowmouse = true; + allowscroll = false; +} + +assigngroup4 +{ + title = "Assign Group 4"; + category = "selecting"; + description = "Assigns the selected geometry to group 4"; + allowkeys = true; + allowmouse = true; + allowscroll = false; +} + +assigngroup5 +{ + title = "Assign Group 5"; + category = "selecting"; + description = "Assigns the selected geometry to group 5"; + allowkeys = true; + allowmouse = true; + allowscroll = false; +} + +assigngroup6 +{ + title = "Assign Group 6"; + category = "selecting"; + description = "Assigns the selected geometry to group 6"; + allowkeys = true; + allowmouse = true; + allowscroll = false; +} + +assigngroup7 +{ + title = "Assign Group 7"; + category = "selecting"; + description = "Assigns the selected geometry to group 7"; + allowkeys = true; + allowmouse = true; + allowscroll = false; +} + +assigngroup8 +{ + title = "Assign Group 8"; + category = "selecting"; + description = "Assigns the selected geometry to group 8"; + allowkeys = true; + allowmouse = true; + allowscroll = false; +} + +assigngroup9 +{ + title = "Assign Group 9"; + category = "selecting"; + description = "Assigns the selected geometry to group 9"; + allowkeys = true; + allowmouse = true; + allowscroll = false; +} + +assigngroup10 +{ + title = "Assign Group 10"; + category = "selecting"; + description = "Assigns the selected geometry to group 10"; + allowkeys = true; + allowmouse = true; + allowscroll = false; +} + +openscripteditor +{ + title = "Script Editor"; + category = "view"; + description = "This opens the script editor that allows you to edit any scripts in your map or any script files."; + allowkeys = true; + allowmouse = true; + allowscroll = false; +} + +viewmodenormal +{ + title = "View Wireframe"; + category = "view"; + description = "This sets the view mode to Wireframe. This shows only the map elements without any sector filling."; + allowkeys = true; + allowmouse = true; + allowscroll = false; +} + +viewmodefloorcolor +{ + title = "View Floor Color"; + category = "view"; + description = "None"; + allowkeys = true; + allowmouse = true; + allowscroll = false; +} + +viewmodeceilingcolor +{ + title = "View Ceiling Color"; + category = "view"; + description = "None"; + allowkeys = true; + allowmouse = true; + allowscroll = false; +} + +viewmodethingcolor +{ + title = "View Thing Color"; + category = "view"; + description = "None"; + allowkeys = true; + allowmouse = true; + allowscroll = false; +} + +viewmodefloors +{ + title = "View Floor Textures"; + category = "view"; + description = "This sets the view mode to Floor Textures. In this view mode the sectors are filled with their floor texture and with their brightness level applied."; + allowkeys = true; + allowmouse = true; + allowscroll = false; +} + +viewmodeceilings +{ + title = "View Ceiling Textures"; + category = "view"; + description = "This sets the view mode to Ceiling Textures. In this view mode the sectors are filled with their ceiling texture and with their brightness level applied."; + allowkeys = true; + allowmouse = true; + allowscroll = false; +} + +visualselect +{ + title = "Select"; + category = "visual"; + description = "Selects the highlighted item."; + allowkeys = true; + allowmouse = true; + allowscroll = false; +} + +visualedit +{ + title = "Edit"; + category = "visual"; + description = "Edits the properties of the selected items or drags the selected items."; + allowkeys = true; + allowmouse = true; + allowscroll = false; +} + +createprefab +{ + title = "Create Prefab"; + category = "prefabs"; + description = "Creates a prefab from the selected geometry and saves it to a prefab file."; + allowkeys = true; + allowmouse = true; + allowscroll = false; +} + +insertprefabfile +{ + title = "Insert Prefab File"; + category = "prefabs"; + description = "Browses for a Prefab file and inserts the prefab geometry into the map."; + allowkeys = true; + allowmouse = true; + allowscroll = false; +} + +insertpreviousprefab +{ + title = "Insert Previous Prefab"; + category = "prefabs"; + description = "Inserts the previously opened Prefab file again."; + allowkeys = true; + allowmouse = true; + allowscroll = false; +} + +toggleinfopanel +{ + title = "Toggle Info Panel"; + category = "view"; + description = "Toggles the info panel between expanded and collapsed."; + allowkeys = true; + allowmouse = true; + allowscroll = false; +} + +showerrors +{ + title = "Show Errors and Warnings"; + category = "tools"; + description = "Shows the errors and warnings that may have occurred during loading or editing operations."; + allowkeys = true; + allowmouse = true; + allowscroll = false; + default = 122; +} diff --git a/Source/Core/Resources/Builder16.png b/Source/Core/Resources/Builder16.png new file mode 100644 index 0000000000000000000000000000000000000000..e0cd55085bc18f321edb0526419357a71fb61344 GIT binary patch literal 1060 zcmV+<1l#+GP)tHYzWjE01!ZoATIy`|9^&m2iTZ647eHi89p(- zIsNb7|Ghw`Gcx{VX8z9bG!4z537ipV0@XZyP`W!MyMj=z^b& zO#cJen7A0e{rkH8_y3>yK&Ns1WB$wf``@olrvHpzco+m@e*umA^Y`BY1_qW|1_ovZ zfB*t|0c-#e{FnLv|M$PYOh1A4a;E(J&m_Ub$nc4ofkl#u;XfZMBWv+L=3fl||NjN5 zW!3~5pao=H1qdLZ7bHNsMSuqE1UlplCo@+X3ll2?+aDeV#{Ue!xcSb&#vsV>jrkqJ zf5zVoKmLAy`S(BLd!Pa9f%>Wd0*DD}y#o+G14iePAAf(m`~MecC*xlRCPo$@4K$kh zFDRrLe*OK+@azAt6d-3c5W9jj0t65fgkUn_WMJk0#qi_TAI5)oIT&~ul>b`;^{_Jh zX8z3}{9l1V>YqBp51?!QGW>bZ%E0`GgMq~a=sSZAtqdIh`51&4M7MlpcmqsIf2RQTegp_0U@Wlr zvoWwtVQ1hG7GMx#0~*Be?f*xH4-5~0M*jf1MVR3(<6(yPOwSnv|4T9eV}j!^!(Yzd z3}4L{82&l{1P~}19f4`!5HPf_urRP0e`0vM_7}tV1N=;4CZII-i}5SN4`v|#|Ml4a z|Nm$FVfd{Blt1{5;o}lum^}dqAW%xI0ObQNU;+|hkdfhJ;I{$V9Q*j+wb0Lu4@J3w z3GL6{KMZmV`UfN!lvO@4ykz~(@Uh|x!`mys@L&K4AXqGbGsa(r-~N9Y{-pv9>t$y6 z#|aD%24H^p|L^~=ue|>SVt}p$`u&$GQ27;*dJq5zAQlEV_z$K3Z2>CW3ryl{z=+%e z3=_uh3~xL+8Tf!%>d!Zb!vAnx00D$#0KzT6F!}zK;p6vr3{TdwGjIq2?fU$a;TzDK e0vM_R0t^7FDpW8kO0&2C0000@r=Z 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 bf`I`5Vn__GT|yYh00000NkvXXu0mjf8QmoH literal 0 HcmV?d00001 diff --git a/Source/Core/Resources/Close.png b/Source/Core/Resources/Close.png new file mode 100644 index 0000000000000000000000000000000000000000..e86f5f6a9928e3edeb4d46424526937762f26703 GIT binary patch literal 264 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`oCO|{#S9GGLLkg|>2BR0kYI^x zM2T~LZf;YIczg8-MWZ;0Udl z#CY|5Pk{430|5{A>kU7+1YRdjOm3F?)G)!kO@e_zKUKe_u(J0z(7_Czu6{1-oD!M< Dh5cAM literal 0 HcmV?d00001 diff --git a/Source/Core/Resources/ColorPick.png b/Source/Core/Resources/ColorPick.png new file mode 100644 index 0000000000000000000000000000000000000000..4cf7c053a30e3a2d156ccb01f87f600ed7cddcfe GIT binary patch literal 997 zcmVec`$GW1`}Z_7C5vB zful@sy~+YPZ0mNs3?xIXWcvz z@?jJE78W?+e28X#fm(?h^YfVP^EhVb;NB>+ZFx*L|7EuS=OI&SoO(ZLG)k^Lx-X3O(Hr zs_rHNhU$p2wvujX;)3}yUmELZ9n#S=T+98DIu-^#W4iqulhyArsg1&xwuhUc-ef%I zf~ddRjQm<52aLtMYc50mM-jz?TB?W6a?O06A#*9yMlJSVvamJAVZ9W}SgtQMvUNnQ ze+tpoctxb^3h~v)aM+Ma^5Ai@1`{YTCGwpmjXrZGqa#Y}hIg3hR4`flI{M>YsCKxr z#bvb+x>}v6sH#9-8qTf;e-8G^h&Rg6jE18di{$#fC=BKZrba^e>!v^L=bq>EJ~zU= z*RX2E3h56jD@9r9MWHTG5vY2F?PY5TYxg4Ujt^yqoz&m;qvMt@o!@Vzroa=G!j*&D z*6`>fE5$!4Dk>5hjYgcxR*969WaMx8;UBjSd8#{+X&xjfJxED%Co#g6s68(DYnaE2jsy71bHLdyOwbIGla`L*!7YdPd@RO_<#NedOnnw TW@;dJ00000NkvXXu0mjfP1wz@;j(q!3lK=n!AY({UO#lFTB>(_`g8%^e{{R4h=>PzA zFaQARU;qF*m;eA5Z<1fdMgRZ-`AI}URCwB?lRaotQ5c4w+r}yux;P1r6#`Xkk?K-^ zK#FZes31X*fvP`c`_A!1ghrBhmi;0C#u@;$iizWx9}BsGGwFj9|0RsS`7D3u?$a4L zap}J77)W=D2&x3W-IJ|Ego0A>0I}cU^#a^i&%{_Q5NtL#u&7=Tz#IyFDbM(#9EWw&ZO{AL1euw}Vc^I%<2v`bBgp~>a{T&XhUIFx` zPS*ME?mq>=MI6ectd;UmYNG<*fi5zwxzp^Y83W z3gXO4gMXzML{wtO-u3_yhd76bt-e6#5T{oM_@QF^(MJF{6XA@)8I3rFSdF;qcDzP^ dV-pSL*8r9tU~{m$x5NMd002ovPDHLkV1oE^7W@DJ literal 0 HcmV?d00001 diff --git a/Source/Core/Resources/Crosshair.png b/Source/Core/Resources/Crosshair.png new file mode 100644 index 0000000000000000000000000000000000000000..40c70d93042d9b0b22ca315c013f237da164abfb GIT binary patch literal 9590 zcmX9^cRbYpAHT&p5ohm}9VxQMS(zCb8D)eJE{YI$+#wBxL`rhkw`7N8WS&&WEGx4U zB3t3O<@eF=k8=;_;q`vKpRecZ`F?+1&%5hrZ^_9n!VUs~IIXSBoq%`T;fswG_}O7} zsRwvNL|A!5fCrkiV<+u~H@K+BHz zNY|s%koj)ig1m3RD!B*DcVZ3$)tK}k_wpzHa%nk{e+(Bz?V?H2o)4A;QbPiglfrHE{<-?}oKmZw}^RtkCkw3w~z z6Yjl-^V*jgQlwBVr6oBMLW?wcy7Tz;u*d$9cinz%W!LA& zaz}f8E%CK(mB~hp6P4W)$bKy-Nu(UOHX>syvs@1 z$|7p)6~;+gCuHw<;e)(>RFy7xX?U6Z_=dO3^m*p*nO>pl9aL?XPLCgI)SY}un$W5f zr_7#uJ$}vlsNAQ=VBum86<*QvwLVKWDOQ{lHl_hbpILD?GPZC)Oy|+~5;bt!73dPX z4qhHN6xmy~CzytgEGN_$9Q2>8s;fYEVq@?Bz8=P7hBzk!b(NM$QsYF247%{J%7IT2 zqBf6pt6eq>ZH92|Z;!X00qJSv6CGlV+h+Wl#KNndJoSIcasBba$tta(x;dlC?&@qI zSy=Z;RjCdEymFO6Cfhcf-62%+rb+nraP@d}mSi`>wv>5aVKZ+nJ0`F(umSSMIGo^r zTP(b{`sFA4miuRgQ-Hi40eQ^?caF~>%@8lxO)mRBb##?Nx*&rEupUcJJT|`~xOwi6 zLQ(Tusg(%2R(HnR0@Bb|ZSX)?&}-hPzN=hXAk3pr{kqH%Kf! zWf6Jz&h>3T5?ofWqoEJfzz#PFSDqFTkCS1dp#JXa(De7Z(KX-2J)Yucl!M~=+$X02 z93$6sB2$E9SpdF$mmVohDs_~B7$&(TcZmYbm3JUpbNBCJ2`I%qxo(Pw!~3H5FNfpF zPuGw@k=t2u@$Rcn{ngT1u1#_L+2)7_Vom}vdJrf_grVHw@I^kX}C@8=8JoAbdq!qeU7j z&N4a^IC+gwKXGjr32eQo$WuD7$Wz!|e&UXCjZle6Zq`X*S$AbdRN5m>BRNUc!N8I? zJHBmS410M)o8jI-zpa}nkYOTQyJECov9~`Zv~bwz2UY>xP5`()K9>ISqm;CadapI3 zlzi7X!?>pFt+8fo;d6z2HC4nTYsebMhsKT0rq+>AsLx7EE@qUfxDuoV%20XPe0-@l z0%A`@(iU~3I`vh5QQn?jgVamHMt@C4Pc4G<(|M9ph^C4_uF+_z550V4;1;|ZV);m8 z7ODP@p4NH(;2ZQgFhR8-sWJAl0gz#vRRvK1Q+o>IiFI}_r-dnf zh8p!1jK-_&pBo*TI=B3ED{swc2tmcbb&L%ZJKJ@cUVfDv+Y;lOXYjR@hBdCbxlBG; zn5!uu9FDF;nX1!+Di$GR+axMP^LBEU@ub-XTyF$xPq4ZABe^orVZ~6+C)BV;LLv26 zx@Lj!2A)E+#6i2vt~ZbU#0uy-4_(k0Jm;n$hQHroy0e1>RA^q7*{0U!a} zCQ(>IjvnLLTQ^onwM!sYug4E*dvDgxIRA;UV${y`7#12qB(66*8dGpk1JFKG?Dm3t z_8e4z*0FT?u#mHF*JLIS7|{k;fpn`w83d{F}b{$hm@7*#<%dfs-!m7_+ zjYbV@hzQ{gfFekCzxPdY1gnW%arM8@VztYUsi0&7;rrt9Prmah zuE>sqINU{*gQIY8DZ#}(t91YCVxe0z9NW_WDmxc2=^u9RDZ2TkXjg=Lscj0+r57z3 z&{b@S$e?JjHBs%36>$OJWxkNb>#BJ;E?(g`ujmk8|sA?llI%PZ%0D`233F7 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 ToCdN{!(k4ORl z0CRD6^gck+w-OgU=#10BtOJmUb@qz`04e!z1sa+n?tUA_pTWjE#6(4fM#lrd9sT&3+>QVYS=Fy3V8ZQwD6dmj+a8 z-F3|qE4NdBUXo?AAFo>TOX4V`Ei+h0uELnW{}W*LuowFfZv2U;3*|cVdFl1BE}#Cm|;I+brWw zk^!dTay$AQ#b}}hcDDWkv_CAY_g%Avz+hDM@k|6Ux7#cStWiOEv9 z9!KR`|26J+(xZCEQ~%j3;pvYX$>E!n4oHV;da7%xO{Ol}M#=ZRL*Ma-AIp0A%9 zhO2n_+2VpM4wjC#{5&zzVM`CN+@8Sn^~K`-4hOW}Fo5gp-D+Q5!MVFh<+!_v?s9PG!RoM!1|XZNHfWTOk3s-FR?|WmDC+@Emh9a22f_wRVJsGlYBAb7 zTy-uuGSXnODO|Sp*)x)uj0&lL6zQ3=GNQ+M{~pv3*e<#=0{Em{2=u;^xG^cQ#4dtN z4J>y)&YTy48mjut;>ABME~12l*0?LbHN}=7+dhdg>>!)z$;8(zxEUMBIcg89Pk{bF3}y)^$`wYts8e|C zQAI^)-1X}?cXwDv^5^4QAvhdRY8@vU)J0Tv1)k0=-X7dq9;OmXg+kMRDy28QpRu)p zbjNh6(>NymB2+5X1P+&G!pbKmE(kVx4g!3-!(kDGg+=$)R=jS1-h6RcD`)yE;+hy9 ztPUlmc9wm*iq`NbwLQ$7^;G+mq-8~rQL`1Vka~1C(dPIL!R}y>kph~vvIDZM*ouy3 z*gOsAK4u-Suuut$!9dA|{-O*9gUZG6>yrcIHP}95)y$Mlb4dBj#YnYW5oKe88d$55 zrj{1i$gfc(EF`2mBSS=&{3x3L9Fgk){+>m_DT8oj>fIeG^77twgt4b5baZ-{Ojr8N zuS9wxKZsINBE9(6$DMJoh=wxc*}ugHO_8MU8bqTLr($5Nx`3h|j^`%wr`p99`G+RI zjP}oCkih0-I7C|xnBI{EE~TZXw-*fRM+-kKdR7u2+uc-~ei_$4x6W;)+U^&WCy55L zDW+4d(-K<_n{cFWCmBMLQ@$#(dpGcURpak`{lM=?BeHy2CDQ{pz=uoiNcHVWOi0Wje}5Oh6?_wrYiyK}u))G_4Q4t8-s54mW3S?{Spk&5${;oAH|HZG=OP1>6EL6 zLKUX6W<8B$6Zz9H7L$pKh|tT|c&jnglh~(QP0r$r0|JFfbLyoa` z7Zf-(M#5(MPzOl9dKU4NtxD(4{*H-9CTb_Q*@EKb2}s%PNY@9n%7oSWL0X@0p!~U$ z((}W^)E+<}E!xfN*j$=pVdCl(6RJTah=l^*a?_tI2seg~o%G5?+34s?4rJfY)cxP64v<6bF`yK#;d5>A#B@7Xjmm{v?h>ww%{UBB3(iL zzzVM|c^Xr<=YB(rn(mqdW_lSZV!0+iS#4;NZxZ2&vg*NW_3wCsLSNTj>Nm?bWg6)4 wJlE>cEdTz@;j(q!3lK=n!AY({UO#lFTB>(_`g8%^e{{R4h=>PzA zFaQARU;qF*m;eA5Z<1fdMgRZ->PbXFRCwBylFe%qQ541R%a_JftF3j6=+dQ_<{#*; z3kz-v1wn#}D-jnWZWP=FZHlY75#5^liC77UNMoUk7E7cl)Ka3qrKYyvfhZn`gpltt0Ix*sG z{obBhT3M~Tlnr653jm;@0gg+*B7`Oum%oq6iW;>H+-a+1Cp2KU)qbq#>V&T0v97KN z>e`i{dLsLocW>q9JDP=!u7O1Ij7W}T-vt5C-W^%1>pB?wMH1odVMNQqtWmN=ORQwS?r--Af445(0t}(o#$J(kV!HgLF&he*FI5 z%$f7dJu~OrPhQU**8l+M`TYL}J&>gj0!W{QC{1-mJZwtr=UY5wr8io@|9t}jFwvi% z7`qi)KNlc(EydSB#UJXuX8~*@rzQsgRS7r`=4j6{mWz^sI{*-Q{O& zjob$x)5Av$kB@tvG?oCRON4srt`h;PKm-q*uwyhfZF4 zn??uRe`BZ+HKWqEG*k|jj%9ImRi-;a{1Ppd?ojyTgPY|emxG`0{E7g=(d?s@l^KO% zvF6;4%E#0ZI_I*u%NfII$3?%^-hU#?pMDDVYPYuwk>6XZy}BB)Dmc%yJU>@IpA}AR z`d;#3`rFWFsUO5t(!!gT+LDhpOA@2q>IVt0h=G$aKu&Rp9YS<-s> zYUQ&_3&yz4_ThpGZVBi(GzlA{Pw{87|7%W&!+qC;^R?Otvc6LD@o3vh8M3sDnesd#RhB;SihgmfE^-^FB(FtXfU6!?F;3#%I0jKYMeT+?jL3Xb5 zS;|f7pLae;yr`pgG{QEfpn%nB9Nt&>QH-n1Q5Bu5$CTMmM_0LLPUnLE(;dX2b!h9I zZx}qUyGx{Kr%6pScD1a%LAlI@pi%5?zyA)_+UGV(O?bj> z>#rGTU;4N6o@m({Cj*(}qil&$)Vpl@wQFqG^xT8$;Jz#s>NWO_YU=%A>ciMge`t9*l^2qr>c1_wM^v2z zky#rfpXBCT$Rd$x3qHLlLsnFv#R)();PKAn72}ou@#y(-Sa8b8QC)h`vOd*8q|pyc z%O6Mb#5G5Z$#kAODii74(RJl!)ISz`gj$V`w0Xi;+IM_GzbOUN4dxh$muJ(ni>aLV6TXhbjLf=T-+qAG2T90V|Z!I@eLhJ5o z;;oKX8~)N?%vaZbP$(!MpbUl@NG{?fAe?DG(A5XpZvA4qqM&4}x{l93^|iR=!y2I` z)O3@5RCe-fJ#as_XeK#IcnANHJbIo=t2Rx`b#d#Bb8b4e4_AvuD-wb+RhaF z&nexEYg1)|(tyNlHTK5P|D%uJPXDUw&L55c8^quG6#%5t-8>nruN zeF&S;I>C^7)J6)`Z#L$q(di}A`>rV~(*(WVz;Jr!vKM;e{euYkM|)^W@>~%Fz87rh zi1XgJbki-oY4sg?2Dg>7c$X@DIBN~E({z_aQQ}qbXlo-n!%cTiqNRV2P}5I<(726r zyiW1!zao4YuBbc$CiFOBnvBGWo7Ok%@>ISD&K=b|DqEVP!dPmw{0}IRod^I(eB?#b z<+go-dDB|YJ-j#@rU~8il9eR(2cf8JL>R0M=oQoj|Fs#JbS!JQx2K~_ZC29Cfb*4o zL;(bSk?M97yaYgq%^eKvMN38}sA2Vz6Tqc!-brF&CGB|z2|rZ5UpBS-aHRSgL-ZTxIk`(9GiCfXAdA{e@G zdTuED9SG%jT2I(0&gNt`uf}u#t!+7@pu~5&>&D)T= zz6?itmtyT?8KaVyeZ~RCCIlF-+_G;Be-k;M#HAN4>Q_$x@@_fuG#&Ns-`;uBI5%=0 z`bf~nJ>?S`tV^aGCA7+XHDfLX?R^jZ!dJD&_bVr-BL@*;XPfjA>knnVO?&$UopbTBu{03SZA`rTb=rcPeW65Bfn$2 zbJo6ElVOXvbE?1J%9PuMi0)70dVhBVyZEfI>Wq;RjhXEZ4U_c1N2lt4Sb?!SJux3k_RQN`&Z<{QrTXH(n+R?4$cL-FWwWQ~y zt=qzmzx{DWA9Zr_>O-A&#w5HnkFVtV2Kk=X6FZ2fqTo6_%NLxvDz(MitRl zf4^c?>9AhH_3A<3J`f5z`759{gofmfS$9tYnq_bKn6765apMx?Wpu)9u=mH71VQGe zp1>h#AjtdgoGiZxmm+)K@4oOZe3YrZP6((CIGoGuqJv!C_cw?eJ;D37k{~SZgAZ3m z4ru>wOYy221$&aTHt~j76l22w-J0$5UjQG0@q1s^v2StLVp-Ud^g%4YLx%yDAm|z$A}nc47lxbo<;@s ze`1wjeFA}{;h+=$5jv#)3~8i{ebmP%0-P_|Yz!X(VDC^5Xhe;IqQ zJA-L8v*-%3uN{7Sskf7F+dQbX%K>vk-xAb8x0;>BV?iv`s&Q)aw>?W!v@GGunoADRmU;2(pVdhYCf{chDwK zRRISC<(3WyUQ{~0?Af`dRok|iJ$n;cJx%q6YwQW(vEMLk=IcZir{|3OE4di4D39g+64Jy(^wtcC3hA|jQFqgO{} zG~a&&HesxfU48Rz=t(jwKf^#Qq$oXH-+=?^l&P`OfK^(J z&&*jj3I}VadJ=to?S_=(eDR%CmGuPi!F7RiqT&jDr)`~B=Vp)kdi^(tze+7%XMP4> zegAqoBouYj##U9=4?@{Fb4vK^f0dKSfXTAbUHMigkaFT%?fH{TESl!gih?mu2^BWb_ z>-qJUuSdt-LVtTEg|Pi!e0Z_2QWvP!DiB#$oj}#oa32d!{iEV>!Zji=`~4jCnz`9e z4F&joGj@?3&sD~qUKAj&=k4t+?+aNl-61f|>2NAoQ`itC(#jlGwseRTeECj`N@akh zQct*Psd{iPNsY^Ce&bTb68D7RYvSDpLrRya%{IZcVl32j)E6baeWjiKe$cK$D@v5a z#Pap@sW%qYLgA~OJAJbd57C?8hJViH8`J1oP6!QCGha#!X*zOppPtqW5i3uqnFq7t zc8`F!=4vhr)=_YmD6rirmDbaKU*@xu5ba=}?c+_`>%YnqALK>qMX=XhYo9Ja zK3$e~QG5cFG)=8e!U+K`m7$UWWt}XG`vile4SJ#T zOH^LTcw(PnPJJ@2Yf=TaK$>*s| zv`Tqnx^r`zZ51xWE9N=zP08fn*5Z>vt=3m-X%$Rcd2SxhCSf}uuPWuP$M7)u&Vx$$ zy;l9Mz!k{Fvbo7V&(4-C0GHJ~zW1XFZ<(SAi6JWrA*RLTryBuGQG{ORz&l5FR!>QQ z`*J>XYR52+aTIAVmEgID5=1r|hJ0aLsYE_YR-((iFwZvgX5+`#@hFipWM{|5&r(P1 zj6kn!Ve2TKb()Rk7XzFAC(**T22J!U%dA4$UxZKviK$WD_b=+|6F%3J>lwl|0FX$2xbeTLpN4a!G4W6=J{yrcib=}zuB%sv3zCpq;>D?h!-AQ%0_)#;|Il-Zol&|O+d#qb1#vn zG+ZpGDmUWnm4kKU2yUMD1xN@6a0rTmg>+I11Kwtw1MyIdcz%6ZQv;L)PnY5-+1)%J z#X;SYFkmW7ZHkOT%WU^cvuf#7evMZ0pt2)rPBPE>t9Oq0UD=(WFU4rxjQUfX-uocc zOW<1k`|1yl2bt-_v%uDdGn1C)QsU&wOcMzJ{R>SNdYxJ7+VQ!e@ONW`18}=wGO`wX z7_91#8Gda{x>})a|4*zFohGnmqi6j%gM=#UtHf8N{CREV@94DOW~HuS-O|~134G`{-|v4B7E(Xu1HPuXkm$&(gC{C z)HurOLiXNL@qe_lKS)#nOte{{8~a}b_0aRHrqDXr(?%`N6uKPdkz zMY`D9@sB%akB45p=h({GU(-#K!Yi2qA?h+(t5NjOHXE|xkUOuR_HumQp)V<~lFrD9pqjGJuZZg@t^9 zWMAnq-4sX_XO^)6=U(-*KNDT*ueu~<$zH5RSnKx?i!zJLP+NPww)>J5oE%xOgi5)> zedQ*jN`)dzCn#Dc0ZZ*7R|^qZ6AKfYLh(5GNntl!EAP`ublM;pOaopuJVxQg523+B zrB=wN=omNdyp=ff-GS?u=1ojUfhO(90Z{!oKH(lMNa}03?D}QGpJDB0qr-*_`wUHvx>y{V|?5$%%uJ6 zC6#O@RnFzS=V!xdl%vHt!$kD4Q*k&TT`CU6UAXf^gM%>f<#Ka@5tZ^`#<2Dgot!;? z2A`WitvgMUHA>B$Y;oM>p!tc7NUtc$1&WMTe9AV`vY$;f$OZ>O6kY~l%+^n9s4zGR z$pAVgCdr*O9QBTh(k0LeP3^06)rb2Otf`4IAA?i(A(Rv>0Px|#oA+|94O9CI(K=lj zu(stdM#6$Vr%&C51ru_3KFFCv%l-rySK9a9E3|J?m?ifR0WjGhAOlIiaul-TB8eJd za|7n@r&Y4us&3rDSH>AeSED-q5&3hDJDwj#5Q|$VJ1BWnYFdCmATwPbDcQvv$W9pP zg$N%E5jf%xr?Iad>Hw%&;Ss^GjQ>q)BvtxClrSR!vv(R{;6xV=5Y z5035vP4*8j>UivIoZK3%Vu&*d%w{K6STSLD#1N&;%y(l$Q?Gg1$klS}I!~tM8mSi8 z#~uj%FoUH&ky&GQ=HahR;_7MSVTk(-(z+GWswyWI1bYCYJ{+{AR@uxz2%>Gl}4is2yCPlMb zZ3mN!N}ddTz0!w`f{7FLKO=8PCLhK)jE@7W#4!9Dn@xWE)%l-X1f31jCY@%*5Sg}F zr(9L6jN{2=kVmq~(gA9r~d)44M^fG6yh;6SdL5)XwE{*!r16{vEbBxk4&^nus z8sUHbB}NH-C^U}nONJiOxeML7D-!ZBElc-PMgnY7G!_ha4qP|)tH11^KcvW8-eHpI z)#cvFbT0_y;x6)3CXBZP02?uU5CXwa$kY5XTxsff$v)6eCu`&mtKdE3;u8-VHL(Zg zyBjGXJ|8I(!+?YwS;EQobtH?Z=9>FkG|(O1fc{hpNTp;G*@4{7T~h!$kiSAmKWNf3 z0x?zj=KldC=7@k9v$BBtK$pKY+m??Z4n8{*-b-N;%+=?i+pcf)oRmmpX$_hqyaV;L z{5wx|Pn4~0o5NEpHfYQ`g8;$~D5PdK)ioUz0A1n3*D}IBUD=FiBQ+f$_gAP@^&8nv zUJnJQ=W~Z1A?tpE&>=Yc>ZfXmWU@8C7$A!%+F3^fDl~I%<})Drc4lhyOqolx^C)sV z*Zafi{2)m3GMiA23f^3;(dn4hRx~F!_fx)a23;e|_Ku43n4yhGc(&MjG6&&9*oKiP z)_yQ8)p{xgT~JQEbbVU%Q9DOsTY{Xx5<2;kP9(jNhJT-z%1`GH;`Pqn--?h_G4U#W zZN4NJ=qv4LENW8n&|mBHr3r=jA!d?=GNQ<|@3X~=|Y89bxJ78ZljV5Je?^~n1#DSa=Y=9nf%)#t9 z*&4u-x{46)UA1)Qt1#g|4jW5pi7QeS+gv=&P9g3zY z-~xm~s^}ANrsKFwGDWGwkN*DI;%tM~=^`BmPQ!XVT8CsDP|0v)K^^giQU==jVoFA8}0c&YiAszIm*SAy(Rt)6Crh~?dJR;zT;d-1dr&Vr6s z;RmrdUO&Ku#$U;0VP!mjs6nuhAazAvS^8SHw*qqWasxumoQNo?4X2a)_Ij?ezax{u zt6z`y?CK(uBLe__7E?B$c)RE{lLa^mK0GN`jl($IlgM=^9UTqbQE?xaT<&fYZjo&C zh2~&_WvJh;SDGggs)m*4rYE*pM^XR)lrIM8FH))ds<57Uy6_7NNVHbK{PkGm80KOdMX+V> zs^%J|CTEOO&+5dWVoNrp`#Z`?*h>;b)SyiM{gkPyLGO+l{5;c@09Rnam9DkFxt{+x zF6YxuNcOm|E!(=dk^jq{fzL{pI+!Uh%29#Nc=c|ts;_uOc{D(W|CSUH9Md?}B2uuh zI`{(x4f0Cj%~Kz=<7w|Uo@FUm1zA@3Xs-e<30oNdCM&D`JaU~*e*l1S@tkdtHaP7K z{tA&y$Erl2ev7Pc(Y10;u;G7SpHA+dniiV<+f8<`o(`I2-`J;5Z4z|-i6jbpbcO?w zD51wTa8|}0l&*y9t~^lJiINTT`1?SAKvw(JgL9_V*cWAKFq8#bMhGO>rE7aXqMnY1 zFOJsM2l`tUHZz{izdLEI#g=wY*P2(bbMW1KfK`KueY!8+6NTxyS8b*oXel;h>;DV7>`+NwRIeTG>QnW%PuGtq=ep{*(h~^=Bnt z43aT;o-T>0F1JoC65r0{KkOC!%Je`$w^}J^o1*edJCBu`I^~CiV>xb z)PzCCApp|n*Q#}JN%1aD8W6YMi@c@XUxn>JH1dON>H3X=#v|OxROaaC)()s(biM$E zL+I2MR)I<;{e#gdAr-@oM}8vKt5<8F**YIY?BwH1yfGQ6*)8tNr^t@KNnVjyhi$J) z7~yAEA^S%yMLZH=WIv>?g9g6`Do^yUpdBH>z*^Ru8QN(Nh&|plq403ddzIs(Rc?fZ z4hUlZkvYlsB4~V#Cm{2mR}> z;-ggzeg3TFvk&W>Pkf5jg2|@fH7Om6qTFJ!m z9RZ-;rokS4=T!x2M}$A)XF@B=Q<+_dvrP*1Bh%)bOrTVp&m{UfJ|@rMwF=^Bd9hcl zR=X8~(uoFim;xOktT~vbUxsh5&3RhMutEMIcnSn7q%y-17rB{45-##65*jhY(Ep5& z{3CYaw->#jp3P28xjn)S*6VIxSk zoZ|SW+DJlNPWA9erN$(qX@vE$;I2@36r^L-t`NdYDQiPr%dvU)E*T3TM1RE0{pm7h zH)M>B{l3Qj4u5($F6T5Z|Aa6a9a~U%!+K0@8-Vup)ppkod4xp4Tphi_4C-L!ow(X& zIPr#NvH}yr7@^hkCwc^)G;^u0w6;8+Qx=;Wl6P6VlN~Mg;cLY7Tt4{RODZ_? zZ{ia{P?seF`Al3rBJiuJAtIk24|D*md>Fge|KlUdNt5!(Pp6SUcpbrKVwA(RaXyP@ z8cEh+=$U&20hSBlD|m=ZOxl7c3q)H`$3Ap^(ikkymhc&m{wKg2s8lL32?_Z&c@lCe z|H8P#b-OJ|{313VPN0Qxf&!?kq(9M9UtqbQGiVQ@Iv%w<#Pqce@gOA;n77x_aR+e;C_VYK!%6{=2xlFe_kwg|Ue|VH!qVl}*$J}urjk>9HQA*_z z(5qT!yEFlOGnINEW0dl`S%|22HAz?Tto4)=NRb1oaN4yA3dfCU!=pmm%R|}lcx<;= z^JK>#8(snf;K`y@u9D5s5@ONnyW;RPo>Ht1EO~0wot1bg!76NFK08EO`V?RLPqf1n zFR6}XbEra9ZFN#S0#HS1O=b!GKwU`1XbMDeRR9$)0oVa6qeI_`EF&UUC)qjrqY9JO zjOvq_a7o|*uYTHGGd(>aO2O|r(TrFWkOgK`eC{3Cmaev!#g^w~0M3_Lbb5duYTw!Y ziNyh|--xjt8}tO309s zM<%^BxUErld7Zt#jCk~{wAiG~%J`Lnj-gSPpqBNo?yI_aO}0I{LrPLaKgK?E3B&n;S3ExT_EH0#c`a(70y7H6y99e9V{-R0i`6Fcv& zsP|RST)p)TUb@&&{OdVIDCOkROhH*nO7{HMy0L$PdbbsVs?mX^*2IYIHX#&psw&of zb3`K67)DO6fixNQF?3zP=ezT$^4l}}lZJ+W1!v}RyL$l^oZkUi9$zWJN-fd3psB12 zRc-e#sq9Dg)k7>XBM`7EZ5L{xOY_~%#g-jTF(_{!><@i8D$M%&NSu4T&PZJKKi3=O zxljU^rLL{ z5WoYz*T>b#jiEHg$=kY+M_J(jL8qI?mu>)&&lS`c;0Ox^V&G1`pX~S>0#QLUX#87l zODg@aMT4&xXbs7E`ug`x>wAa$jV#nX{B*IgXrt*6oW8CUJ_Jy45-p4Vyh zx0)bA;6_SX8x!Lka{#66S1;&$fpqzse1Yi!=8l!l6ZTBae#&$(LW6js7gHH+TYfdA zF_IMYOLdt^+WepWg@6X{EK#$0aM>Lk08k8aZUir96x#l00?iqWx3xX z8^JSZbMg!ouSb5VYj?jkBS;(z+25;8|SA|Y+Xi({F#v1&7=XA;F1V2{ zzAlww)n-Xy+>dDFaxAUnq0I31=nXb0?+x{q@K~awt<4AqA!6F>hpFs5A^VT z#Vxh?*KFp>rG*ZgE)7%)0UH5A6wAB{(qxwX}V1|<6U&o~( zuOlAvC>P$@6W&LQo?TWx@U#BZX^s4k z>zp@TCqA9{Qtk@)FdHI&>lA|!UJyKc_cL^;Hmq;51S6UFy5qoEiB$08+yu5lWZO7! z-aW8600X_f@E(lV?*ON3z7_bS{`$GnZE4IzSb}cEyb;)3vMf2f%H>03_AKhfZ3Ai{ zX(}O;oGlP)C2abR?!Tz@1q(gzslk?WPMA-pNj|LjpM&F@fE~Q@(eD8M7eRu+Gl770 za0D?ADo}?K(!CoJIG(Z}6iAqq><(*}>PV)2@{fCqzJF*%GJir-^ zw;~huQWNl~ce7>$!Jf!*RC%nqW?tJB6DMw?;6Fa2;u<7WtDjy~6aZjmb<$|xJMdX@ z@GVdPMMT)qIMZ4!D;Ke85A8Z=66PO9{AqxR=0~!jr$NHO67K27rEu4U!&ET=zOhI* ze4k9~fBay5sB1S`|>9Lf02+g7Dgsmc(JJK6;d94iD&VNG|dn~Q`r0f!Y%`o>6DXM&h z5D%IYk1qLXg*JqlI)IIdtvCgRd#UXzOq5CRgT}lg0LLQ$6@Z;pBl(lnex!j>poSUv zWi5K_Dq~%jT9b>GSXfpr!RoUriLciS8qOQ{Wu=|ZK2{@DaQsOkx^iaU<9)1{#nG%; zP(iiez;kFvDSWAoSyx99lUYnZh1+qyv+7a_m&{Op13(m4V;k$sqr3^igFy?yq+JBL zMFz4kR>u6($e9Y|hnz&3U(yVRiC_dim&yAGTUdwsD>D2@hSJ{-jY%(l`rR>;XMw{e zzZ0}NBiySg_`%r0s4KpK6S0q*88s%$*@ASPi~d~P+)nXnrC$s6H&&roZ*fCd%JXzT z;!6n0gh$YNQKy3MtX*2A|j><9Ds0GS6_t^QRztIt_M4f&&OOnWsv$`_|5X!yc$Tdg3Q6I|60 z^S~2aXL}xnLq%(O%J~$X!$kEF1T^0b&>Y95-?YTX*hH$oidAZQ(Bmgtz2aCfyJHNQsCwD{*cA<-;+XCw`KN zc-3fn+h7Enho*$Mc^0n@g?} z_0DGaVoD0NlGXvr6i=4bUxd3LXf&pr@?4boKbwHgJYlE3AG!d2Ai*&rCyM6TCNG5x zOmg@?q54^UK8B#~VL=;|CLQm3?wN;A>uv_UXZCB6FcCp#@PCX<-Mx zGxzo0Z`}kQpaYwId;iDuO?&V$82p_1^0E%uXTS0JHm^cAQ#*R`jZEk25f-KX}wFZ@9e59J35jrC7*_jCd8CW=hIOc{)| z#L#_NDo8-VmBwtorhRxe4}z|qT99rujlqKeAS2Xs^)WDaiZdWeRcZ@49zlIrYeX4- zN)Xjn*PoLYQe24bd8QGGu!8UIrU8OJ>hXF?<_$5Q=e|i&+k>24 z+pPiH%G1wUQ)F$G2EG(m)wB^2kSCbYT^3#AATg|Mdux@cfBNJ|)2*owzc>E{R}Y=- zc*3L{)Kc^`6ksy=%_k~#&URieJY!~76@XYk$@p+U;9$Sy_pTVB8HcyIwsRP@iY3uH zf#c^y5&?%!EjP$72BDGuS=(CCO>m0vLW$fr#^ zvn(D}(l}}=cm?WhKS^%;VO;z{R^L8A6{Ap_5DI;4lS>@Kb&2Cb0Yq=H05}GHg7-)& zWH9hKmlz>>g`3iIUvPOg`fjnD=iT}c>r{Yhhh)B;l1;An)hht^%km!Kx*W z{lFnwB_s4-E|Lyult(0*z5{q!M3z{r{OP;PF3T>yym`{^mYdhLW7nzeL!@4UaBJW* zv{#L2xUho8g)sbj5ek^Ut*K%lU!I2lBTZ{%ZN=sG4pZ>$W|Gt+(HGi!hZr zyB2q3^jL;tr?&wY9exmAPf&9WoqR)=wK#CuulNx_7hn*DM=4lJ@(8WKYA>$_5BKdO zW)n7@1xx1iE0_0aDQ+LLi%XNUYxVIIfLt55xb2wqNPZAIZmWQ}ixhE7uTYJKBu>9t zp*Qx)W2`xQ5}K?*e!Mi#Ee>7cjG_!z(>Yzz-*Fou67uVhL zdF=W!Yw?O2n-7mD-__-(K30E@c`Hl&n{wl9G<#~O;$q%g1oO&1>GzHc`r|NT_U*h?9pNF5!M7IbjW|0{)BSgSi!aOXA#w|nqeprEsP@%w}Pvv7j z4eh(8)0UY49m8M5;;V5r4+cG*g|y(%(<}}mOTC=Q91q2Gl8cl=RENG*d`m$RoXGDG z!aoQa!;5juMRj6zNPATRRgAd4(-H~hqqnng%kS2yhC_?6NrdC#W!Btbo}}o%lVQ3x z=GiX9qnQgV*RFwPJE?dT{e%EFe#Niw#iGAHDXu``TZ_#(D&Fh48y%YVoEs=+-Z|LbC$y|sR%KygZxJY48lj$vo?`hVVIyM+jF$Myq%o8T936Zsh;?LOehgP zvm(ZZ546@t(dCu+XeNiHUle?T%;A&~GRZ^cG(XX%WCsHJW*fHI;q`RHDj;E*-|gqa z$|bS}vIohIgs`8ya=Xcp#w2s|SNMATkNw-sPGxjnzSTZHht;oHsWab(-wFxx{f;!> z+`F#wy-hlxFUC95ZB6HP{h-f>UfUnDp-AOF__hD?t-bb_D4nwrD#xD`+!rH1c#CCB z>+_!3VyE!5Gm%e0RZNC)R|Z_vM%$^`wF^w|Ii*N|F>&4pwhZAB-aK;U5=k(iNlmG} zJ{ihC-9@p?F*5kf(sJW`XcYD9Bvb?d-pi#t6-pt$+qGFn=zldvhUngMr$srMqQr@} zNJd_PxNa?8dr+;w3yNZF8P#4)2V|ajVpJ~l$@zGF|E32WXq}k;dwFct+8{Fe+NhDu)C&m1Ej0JF@q z`?X)8VOFCaKD=;E=*ETZt?hQH@Wg)>nr6o9tktD(+MeTYFsc{*u@U=LtZmDBc0g(T zdG0Q~^YRwaC*EsUVW_I5h?9|Ev0kYL#&*vQMCOl}B@RGJO-(WbDCF4Tp|(SCoq76Q z3AH!8z|~#0R8Gb!!LuP6jshXub;XovLBjtL)FaB>UsEOSg5Fwi$mQ1JcQ1jrG)Uk( zjRaM6CAad*=~{;dgIKD3c#q>(&VyURlE_MH9gIH9&Y5~-usk+_DCOjWgO)DWlX=-o zp7h%pafUSf?hgi>^G>cLkv&mqy|1ZB;{{R6X{L51*?IBcAW_b6p6{M(ozpRtq!Zk{ zbC_K6I640CbJ_C@OJ4w}ZEtxi++?x<(v8djKWuRPMgBK4US(l=Y-C@Dha2nm#>hPv z1x^hNcqxxwbQzHX#!)@94ULAi-T0Fcv)7UME3iUcIv@p>CtkpX*gf5nlpVp&4@H< z)1wT7h4YtQCCgn-bsoA^Csdj`mTf+}s%X}}+!9BETfZy@YwV#P3?Y?b<|lTC@f1xe>_<=Wq`pE%BY&DTn=bwUqX=QqDbydi=z za;U8j1fP&oKggfEt@4+adqbwQcL_0SD9)faOLs+Nt6G5O{>h2M(eYC^dcqEYh!_oE zpEkvQDeMlA0O{`+*bi4!JjunWAE4JS@MVlAg0f)c>qgZ z%si}_8K)5;JpmZk0MrWT^I;8rL6rzaqra(XLe3GzsMY9Y`_4P>7XV)>VH9y2z=$JN zk}$jJYZY#-xUG2J&-wy0X z)=krQA&|uHXESX;Vvf(noj^~wW=AQK=0KtzPWzoivP>*gYJsdayhBjMA;6BQL@ zvXdvSU+iLxjHHFayC#y}1();lU-d>Ux3CWX4%V)aZFZbW*JufpG?5aa)fw6XQ; z&z#ApSmi^0Xq~uy>(BqlLphI4z1A{_Zf)(kTQ&d3Ac^6nv?pLs&RZnLctwlJE%Z>Y z<9OXWpKwUrSs}xd5ZZ0(ay85zR+~!ykn`|?HeMaB{EFmBHSe7VNkc+La{T-*v(2IM z9gS^P5-8kaL&+)s5y4Wb^$m(k#u5DM?8DE4YwN!+LcU?gCZy`lz%Wq0c*k!5@AaK-XLVY|Ja^RDx?@$VH%af_oA zKyd#}xya~-YG`)tB65(Wk}R4&_ha3#nGd+*_gLB0qPIK0hOxux9@U1wBHn!-zJ4W3wFhDPfmc1^!jlt_wdC|V= zqkM=p_j>fWCtBQRZl$RJV-@3-t^es|*2%*>lOQTb@4z#_x+U>WMwxWC^ejl{bid;~ zT3*xj|8aB{ZcYAg8-BI{qq|$_9^EkFOGt;L;E-;V?$Odx(lR6jlrE7P z4N8NQNGgqhq~yEb`zJid6UXPd@AEn@cQYKuUM)vgr#eV^*(;6cZn2TzuKXjTrPNz z<8qM=oHMRE4mV4wu3fBe;HpgoDfT0r4nT3NFEIE5#rm%16`x0cESM?oJvl#bbG=&+ z@6nX91Q*omasoOX))gu^`pjspdzNBRSuxpc+&H&@QfH2vmC(`{E6Z^EiS z%uIOWuJn9z4-4Q@Fg^8pr85&FF4Hfu4r4LS^)l0bATB7x%_p(zLe$ z*D;ct%dxFzF2w&mkmbYO)~`6ajT>u-X64v5R8A z6BR*=u_cW^0b&Rk>=I|_DlsY5t?Ju_Y(9hK{ZYU8Ml1joN`SK#Y0^mjrId&KrhcjB zcLXT7Vfr7J%u8tG#Bm<+2I3^E>*(Bn#R2zAx$CCPa|fSy=MD)o#<>_VO#)Oyo*8T4@$xV z_a)=ip1u6SPR8$okYbD3i0le)F!C=yqoMv9q0(I9k+gs{fTP$D+T1Tvr%>h$$OosnqyhQzW z@PICW!UKN1&6}8>t2O>2CiZIdHbc@!XMEzukSQzt83{##0ZZuHRqr}%Ns!r0ue2(M zrx7F$91ib zsG)x;?##i201CN|VnL_7(gfRUSRlt&>Yt7rw6U9aob|6*2Ar7ocgNCn2U%}+`(Qcm z3R&~IhaQp>%%M`?jrN(IbV%WFZAndUF4?Xe4Cx619$Ft+MVn9BltxCz%z8<2$FSrp zf$)n&J#F~%3ty==eLFqs9hg2M(w%rG;ki#_2@w^o52(CL-T7E&mwcc>6c-7W4TR%< z`fYP)mvO{!&9zn3S^Ocu0Ka zzIMAEQBMocwJKT00@Q-cuD^L5?O~U!^EG{8(4G=+*OSHZenGj~SWl9~+XGO>hVOPz zA9K_si*Q23RWQMaB-r}>k8~L?%g6fc zE0s|jxn|T_Su^ok0Ym1CE?cD)KU4j60559UfwvkDiShpeTLXhJ;reIE>zQ=|QkFul ztyC@+`1AlWH&RBCh4L-)A2kzXb83QTNRcXn7)$9c@}~C58PW#Id9j*tweq8mM2LYh zH7t^fjRj7J^q>s!ynYi)UhS@(Tb)29!Aa5pN)fQVni|~g%2N9I$pVFSSh`fyJMru# z5uMsOf+8)*t>8MKApiI3c9lF!*C`GmPJFBXeG&&=j}y}*BVJHv?t$vq1ff)Q8tPD1 zOvn$nKt5O2@qTx|rDdrgd;hLr@7MIR1f7CnsCPU<*T#X%Ddokc(@SEU=C!5Vu|-0p zqY!Qi!qsKl&N&CHPdIyj(61tj9t;EyAX1QIU9S)3Lp6G?qqotTpCd27MWIng45I)( zYm<-WhHI*p3lYBUwzNA)4I3eF>FSgg#&Qv>&jsT%s=vg&Dj@uYvm@2>q__Gv5ye`w zwfmh)_Y~n5N6(}0H60;q`^NdKbps4yyq9tc&X2i~@9Om!VrEt(v8%fE*uWrt^wquP z5^n-VP{L_$PECZpfQQ-y>-+mMe6w_vskv7$F4BqWqecdNZ^9n+nEs5rS!*49d; zVf{07bIJ!n+<3l?l%xRAHBC1j3`pv7Eu%^n<<*kH;=M%Ddy@_V{V-3p|4)C*~8~P)ZL`**~+YJ{o$qr6IU|Z zeAPSa3y}W$wvxLRiCm(RR~ zZ%vt_uJ_~FeR-FK?iHpuU$0pIS5NPFQndF$r!6S2I}2MHO*gg>_+-Hrc~K{xhz2TQ9-Uhb+m%Y)m@O&G+*= zHKc#F5QeTzzWOv@nggI;`kraNxqF!tPsRB>(5r5!<2?D?#q^V*^I6>y%Rz9^+&-(Z z_TPX-X2)Bq*@>wYHjf1-fdU$25m{v`mI?+pu?D}TJP@5!m$*YLOvr|+8Y!vVcxr6y z%%mfZF2A=g@koX3L|~b*z&Yzq*(Ih8#^{?!M)#87>b}MWby-Y@Y`uQ^7vxPwWKEg2 zA4Mjqt0!@8rK4Z#u#9JUtU~?Z z=a1%&ACmshyk&bo&J)%>7hUmup_l?8VfWqXigpH1^Nh}U{kVWoZub+TZBpX53hP$- zk|BhR!g&Ltl^fC~S-yMkeyMY_x?4aRGe zTg{jZ#QnFiK3aYN>66xW^N07q7nei!S|WmYZ}|$rXd%JzHo*K-8#C@vv=nqpT!|fb zcETz^Q)FnhOaQe=AsBvtmo9bEBut)Q0C6H?qEe9ha5!8D0Mz)q4*Q6h^*0jHrJ8WB z4Gph3PBSf)t1p(p(xtTs$s!s6%1*-gu_ zg3|e7qn_obv?mZOeo|^#?)GHk?u9b+_)?HcBbXMA;;#<<97X{ge`5I7IgLuz#;i|# zMj=y!j0 z>EMYZO6N^1ob6N} zg}ot%#1A&q=#Ud^j-0R7JNY@>xv85}wIhv=t(odAp^N7IgVowddOc-XkC_7hrF#sA z%MO6o@|Hfr)h(q7*AgQn+D|06Dy`3#?3yd%o)-qACN20_Z~z!@Q)qo}zOot7y)%k69rU)Q^DqUSFU8O2t zrFOF}+6q0n9`=^ONjj1a|7%5$@8HKx5XJ}-=5MoRy6DNJWW2K?p`Iq?TMK7!$RI#D z^R4~-fQFao%Y9Zb46}xm087s!ZW=>@?O`nle6&#DHuE31#2la1oY^hk(x2Q5>Lw+t zF8*XvvAKuFO)>9RiRL6xBF4Xtg({Gn-J9r zIWb&RoD`NL!4FV=0X^EYTNoYjj{=35HEb(z@6HH4*%+SL<0m}AtViNIH+Bli34qrl zN;=<_od3ig`x}VTb%BCV(yec1qVPm~ET;6<5D|D`aT2_z)Gd+L)2Cx1Ciu9`M0cQM z(j@viEEzFZC+dKZZpGGyzj|Zpe-q}=DNJL#D+VX9)=(htU5c)&o?Hn=wdD7?Lg%yEn(7LD>jO%@tj>3~XL}@7JE*!^8=Hri9 z{z(Yh`2)*iA{ZCs_rpMQl_))cmdE7Ffd2+fv$-|cE~@`&U~Z9lambHAhv+HW@g&9f z)x_2K{ zi=FR-EQAsD@@8(U?#*Nwaqk8P154R0eV0(2m_VFHDb+JVH; zXE%z@t%-{HS;oQbO&4c&W9nxR8W5}aXC1kc_U~yU{F0Kp_dw(7me*Lui~T%f@7L?& z3txTvw7RxG1(XvfkaTYk+?}Q92;_XYjrf+O3*EoFUwLb$&0N1ogq-0~z>I?FW2X;L zy8?wF3cv_HR{qa|;FkCjSM4G_6*BM~zD<9y{VNi>EXc&<40c_WHrN;hkSYvrxRU!& z8%k52FmH>ean)_hA#Zp_dNZ{O!MM+^QJE|cO+L%llwD8<|FskmnYVbdG9X?=^yn^1 zS~Fv8YQPnw^;qS7yy|m2_;mb{EZzZjha#?Itx^VBjFBi7-(+b4hl14Mg*TOY85C~# zCO~~#O%HAQ<*~gJxGWw%cb=oj%n23T@zQlc07_P_?ZPaiK(TFJ#rKFM3+m-Yh!9Z{ zDqm9wE0XSc@D?6b~t$v5(amxjuxsFXw+_Af`}nHgM)n}w3h^{JzZ~`fV7tAnnVnHV3ps$rZW4}TAKWv!jH4g_T#FPHR!Mwj{9B1yQ z=Upz!q-XlH4iO|^TU;&q1tm5~2_&2;8b%@^UHJF)ciyu5`-k}uRYfmsOK4u_XF>eV z;G3|qpfl}5QJEED0otY1Ja;=X@JrPf-v9uYq^~$nL>`Pnsu5(jhj9bk=R|Tpb}k=Q zIH67VUyfye(!aNNss&l3AB-h9_5?o2ePRN@60d$|{?rwT0EL#HA6hLDzjL5=yh+nZ zi#@fPTCZxO#w^Xmj?h@cj0GyP%^N~~Yc+P`7@X+eUCu8M5R)jR`RAY!_{TtAApdA3 z;=k|57cYb=y3-8ZMxuH*^L&l~O1z{c-gn>2MspDgfR17#MROX+Me1SaS2g5tds!nd zW~}gT;RDl>?;^%u(rrXQaJprS;WC_(50}qt7xbz@^xb&*sN8k&_X0a_sUy~6#MhNF z%BRFoD)D|CWI1=MJ6KIBMs-k>AC`;b^;YKkzdr@kE!dl^P^RM?z#EC?*X>K5eo2=mfWV?;mq+M98`@at zf%3~w_YoX3$v5xjRxSt=>$@WUo$yC-?9_iAXml^S+D_53BcUDNkiNaGm{W7YekR#u z)AbhuLWxM7dAg3g5Gx@;4sbG&-d`gu=nc3|6PN)eUjQ06!6(Ng#$9!xK@bW15|0Gl|Pu3q599VXoOY_KIIFB#|t36KK z&H`2H+dglij4LI3f}9@=odhV-s=H=qf#JGe^RdCLzIs)u=ss@Fl8x5@R&-l%>QpSKZy91n*ZoF0mXqE6!Gt( zdEpy7A*Btuo;$1Sn<5w}BfS3(Q_q zQ2;W#d_bpNJg<`?D8kxJ3-1BS@a93QFP99-M4^?#k5#Q_0zXCY6~-*RtoV-^1pCB- zg=ZW=KBFl{Zta}w<5P($|@32F%#HX@nlXIqVMMc7=Nei!je#XI-g&Ufa zlgJ4sW0 zG)0aK|4C=->{Pbw4T#73rqs^n2*`>&zqP~B&rjeeic3YvX0R7Tqqy8 zcaP+$gfe|NAj|1HFXx*c?g)pGXcd3zok&u3D6g4kB~40-Wk=LqS-Bfv)>E8D8`@2IF1RSMf?xaADN>g{)tEbYmZ7#Y{rr`~VfffhA|HSgT6L6lmm=xSNQUZ2Q>SgLldL?Hss* zzoJB<33tFp^wspIvgWG%d~3U)8i zL@s44^`w;Ip*`Rrw__=>;%fi=_koZKrhTG3TaJ4B8yi97b%aTfBZ;QdzxV9KKQk73 zhMIrnJQ9UNITSBh2F&5@<4K1CUGU3}3

    sl#SISj9<8CvYu`2Fz_Ky(nh1zPQdA*-fMwGpIdvU$e)JO~Mirqoc<(p@n#Ig@> z2d|$J%f|)$#LWT@q6DlLc9c1 zNK0xX#@L)4-ne^Zh?pdeb*|`HhwIuv^=b16R_99@-GZDn2J_m4^A;8|>0(J0B(c&) zp7^=R;0s&o&s^BOEpQk!xWY@Cyj7F1dJ+-YjB}vA6VF9*^`q(`i6JeFXz)vO8a6F* z>R{uI!N1P|U!CHy1@zp9;)f-uB%D-by39BUK+a&)v|KT=@i&zy-|ktJo(MJrD=7{v zrUd5rYI1h6X>vsuS^Ttw6-UZtJVIt>0cnc^AoM-Xr{28U3>S?`9g~pj3o*q4AgWR+ z`Du(2g7s^3X*^|EpW`k+6K|gK$Fl6$?QXi+gcj6)mE8va z{<|)dpxJ4Vv7sdzCYSn4`V}-8j{D`}<;}P3N*kt-QcfSePw}21-FBDH(%GFly}i^# z@flD&^ePCZdcR2@&MIxV5&X@96xiZ@nYXcpS6J`}s@3P-z{`itTct6)BhcxBX^hx# zrA0}H-Czzm<{9BNqcSexSo-iKUejAsno4T06nlF%`u&{8LVEx+W!QJsg`@xzi=19+ z`_V$jK|9vR=Lkv(w@K>Qd9QBDSJQzYhp30!&|N-j|Py3y(4 z?wc$rZlUqje(Sh{X$b2yt=tZb=OvRiEYHbrIjUuTdVg>mOGRc*V;%2JNbF?A-YgsJ z@4q#FD=b_Qth-@Qb;h0$D)&EF*+cANqRxOqM~N;{oDX;ut^F_-u?rj%mBS#wfUimg zo&x2lgPbGhpE{f<-4`P9lLrR2g6zQ5a3XLDu9NKvIO`h8!vQ6IX3h&t7UnKT7~UBE zl@pUD8pJ~khDtV{BcGy6oev`e4Gc4~eEEzumPmoztgo8hs80RQh^wj-s96#pPR2;MlP#JU0Vv0hsKG3#*W^7U!h}9%13*zuq04 zZQ2Nt#3H_=iMjXyY?$-Vs&e06X_x@|6+{DQLEgz;1bh^P$Yz(hLhQTNsw~}e0+UcQ zBd(u1C+W_hz~Fzg%tRf*^b6~^;yhGdLV8I4te(}3R;{X(5RR0+khVcVG^U4og8*D z&u1S(OZ=Y!9sznBFp+|c6y>gxK~)2$0B!aSh2t^ijI+IxyFE2ZJ`*}mx4f^9>CcL5CRufGyf?8P!*?qrzjbVI?X<~xf4f7 z(Xd{!jSmnzSqlLOp3gBvZA@mT9%0jr@6etc-%8&p*#0YBAtJkF>9|$<@S zT|GqtJF^p7tW&%mlN81;JoQnM97sp_HVY_?wXz)4^a6s8?t(5|Cp|enb&z9lFl)f+ zt@5Q|;>Dd^;+MpPHKC()GB3orqKL)&6dQ=$I!t}v|Fzc1=!snwUiu7C)oy`%mBs(( z#kpny|7c}Z`u%gbj4CK(Y(a7K5b@hq6k~mumh+KdT~PA6A;TM6H$Uj&-EFqq$X)(` zue4aqi_d(Pk>i$@5ed8>i(xJ(O5l92JFkWW?u>~7F2)d#?LLm0vc4AX&^|>Tsgr=& zYixcNTNQnMf@;2FV6ck1U!wmheAyhUPp%9~)}tp@{mz*>`U%`Hy{NLyQfM1{iVbak zBqscEkCyl22~OQ3R@?>3mX@%eZXuP+vo+3cZZhq(4_iIuC^vk(%n#JZ<1AUjd%l{7 zowfPVNSmWPv|60`MoA+ddvjBSIr)a0D7m9TTJbvbgk!f7c?;c)nwM;#V z9n<=vWz&K-u~Q*)w*$LB$%t8TvL{m-+XJ2^Q@CyejUp=s`A)xz2(}{ldageeL1=wqeS8W;^pCl-z?b1WnFwy^oHpss!tX_V zuVxPN$`Jo~ArTd$U>W#=`{?ctq7>}FWQKcckW`WR=vHW+5R*%P%MUX>?SwphUB31_ zYX1EBv$RpQ$wB2ow{uUqAIxL?udav3WcZ-bp*BOcp(yY#7khAQ{D2PtwA5PzuUpzFI6{&twxvw)yVl;IW)Wh zhG+?aP_RYGAG~4eI7^J2Pn>z8ZrNnvXc>ziWFipzP}!p39){b7U5>lCf^U6hGE+k6 zg@EXC7F8JG`Kq+PF4=(v{~i{rEDb*{|krw4~YS-nQ3MAOrK zGL(RdLbrt`A7FY=r$(NaXRgxWbV`4zG#+3%&{o(34P>CqH(FqjFWcA@^$7?+y}J0ZWT*;j?kqyp~8H zA_WeA0KCPV1mXLqH`)dgd0FU8#0zSLnj5NFJ11<}C?1WL&jW{&oLIsSNE^dgjz_o6 zVL3{>#5yd*69ws%Xu;q=5VfG=^$!Da@;yl!l2}%Sr9OWf1Qz5jhHf?gCz79_Wy3uU z$i9R9+nApFaWnDR(UF?^&&vs6^Ka>VQI%^}wy8^?@iSpsVmHR}z*4IyT(BLW@3lO0 z=9>i_G~&hb+&>E0& zPUz<0RK+K*J}L5D*Ymo{{Ss+b|L&mfPFb6q>HV=N`3H>Ex3Xqx+ls&jAnpk@6%V0Y z4K@visM6FX1p8n!cpK;8fmiArwkikLK?3lJ&kXb12X^epQ(WhnbWFjFhVP?Mp95#= z$C3*2Ul;nv4p@MtiNW_KPDBpN^^j!DBM4L4YzsjQ;#p=LrA3nJ#lD z7)ty{cv-Y2YhE{arDq4ACG7mc*!@R8wl7%p#a0LNC9XGo_xYxy=hOjTSH3m zE@HAQpPFyQV5+!z)U>{-k8tA0BNZNih3O`^v+!|}{4K+7T^aU6 z?&lAG`=B83yAV8!GIkwv6I(KAoom&v(a=v((lS9wXXg6$;v|1zRAwAIL$GfeMA4-x@^vvyCS9>$@j{`M zc^2j$w%)YNv}q18hLWGv*NubW|Am88V(DF;9wxi)4TaV|7u6An1XLofLa(6~U$)O~ zeU8b1SsM)iHFuOh(_mjXqz+qO70Hh3PTfZs5Hp2X_IzaqLDq6$c>pz1*{EO)iO z#L}XBLyhZOSF&jjkY9>8kB1`wAVbw@>TqIdtMEslz zhREzaAp%%|&vfJQ4q^)5auh{xbIb?G_5PbN{Li+FAK&H?o{9i}0|HFs{?e46Gn5k; z6|5e&?2SK~+YG@1rh^UdcUIInf_In?)`vJg`zZCMM9-CmsFubQ;|P(tv&gJ5dk28FF4D(Ee|tB)jfH3H|*Zj(8kS>4l$#96e!|qbNtbf<`DE(|5%1cQ3tbsE4$CpqK z0BJ4SHR0;F5w=YE0!&JBdYuC=L8$9Y)G_yfE}MwKyu(2`-9z3GZR5t%+p`R(ZgAAa zM;2tbI?%s;O$Y)6n6QwH0IQP-&^mp#a zp`QMYjBzngxD-Qt&65XO|q%^OQnT({8nI^h$x17x)Mh_d}lC6Z zwIADDvs=p7_r%WKWe17vaxs^=NO+w_~)j7>zsGSaz2R z!<%vGi54oN(0ZX~xI*F`UIv!(MhvfhWdys_+S~{fW(^lx)+H91s2g0&2E$XI0|+^s zh#b@WR_R4SYB!i)yv<_G+(Fc<2JGBU5keaX6?1PaS7;g9Xn{v+VRrUCeHJ2?PNF-g z^#|q?W&<5?$NWli^`tUtyx1?C{rZ{Ofp$6xiH1q&R1f6F@(z;{o?CoW@y-rFv*yhp z3<+%8ThtG=zTGBf-NxN{MF}c zPCS7IoxZp7K7`An}RFN}F@JZiH|FBkej{y2nG z@KOcVEygClk9d|P2j7oD$1=X}pNipOLAj^-K)4!Mmwf5j5b4u?BA+J>GqGw}jT~FYx$ctp%iI7!`{UvzQY^KKyjD+uJ z&biv2Ug><=WVpNU_t|H6xj(fQqNVt_ZTS%(^S0FV=?NnU-C5i^7x#r!C)$G*`JNDk{>AE7AiVQsu=9oFxF0R(4WtBvB$Bn>o;4_sHAe`ZY ze%8Ks!khA5DF_(BZ}f%N=9Qurxp^DsKW`Q++z{nyE@0<|StI0M%C({ti?Y#se^Id@ zliqLh9K)K1!9shZz{7#{vl&!r`%Sm4$H(sauYG;1?>E!pozD8ivaiLsl(s2;Ej63D zb;^ghi`cSPIiHeqf4LBJE?N*gNOybp=dk(Joyuyb$e$)%o*uqc76NT!_s7f&LHq$WvQF~2SW;%YG%LYYlHfGO zz+35sKA|UnW^bCr&Hk>1p6D{_Z)ak(+QN7)G!BWpoX9uv#^2+I`Wa)xoxJxQOsZ2S zY)HIMKgQ0fQWyn1>YokE6VZ+ULne)1+&(Lz;2*XjWY&)Shdmq1zxy@# z3y^-&yF5YqiK!10{I6FiLR!NRIv{9^Q1OW~Ji|q!dZa7wFIA4g(-2~v0&b@V1sMUlWZXj;w%yI6 z%-(;g^haltMthftsBBCk(;ca7kDhe$`j?(Uym<>eY-qYCxp*FCj;Gaki@*F3?y@J> zH)&VrBl9s8e;!kY5J6{EEpABmI!kEx2kO_OYiBmyWcFs9sh-dKj6QL8OfvIC&1 zTDEs0YGKVWOyQ`18vhn?EIM~fOOrlkBsl&Uc&n}D>vP)`Sk+yRZ)@vYW4up8k0@%x z*)sn2l9Tb~fdUJx1%qBF@M5H5nVg&{a+NxYf3p<@|248qCpTDYs^!nDED;=%7;$78 zCTmBO?RUbqBcV(+yjS6YpBk~oqyf@5TNHqqxpQ^OrnIo+>SxD1JuE@|ytyTn zgHN8fEgeD&C=#WhJtfNGztFB!suwX2wx=%1c!>tCbe*h*?Ti1 zl+G@p-S+=8*cv5%OZ~3C5$*vE_DU6u55F&c9O`tKr~2s32WJ5FJ3}3Qr`JW$JCXM= z;UrtP){E^+>hh8hULlR`h2MBBh#iq9A#u6WU}!veDqWM1xuO9o*CD_G+{-TN@xK;S zHWTrF(uKJNB%fz2%_I`%X9!f&zBOD&51K9|b&DP#Aab-OY4khuFyO~ zRAIzxH{u3t({_;HXBweU;K!rc3^^%QUq!cd0&)Zix{!;NUy2JWoj<}LefU7h>8GZ9 zSkEu&7Rq@{2^1fbwRX74_9}NxoX&r_hJ?Wu!9=5sbAl>em26AT^_glm3EQW`L9DD! zpIF48SK0yHu&5nv`)7sCc=)jWq_}b2Wxjn#1AJPq%Eu!OI&%`U9^|z> zST;qIF;g-8Iy8>}Zp7+)Oov2M0M}d`J)3I~cb1x761bg4BOYp=Qs)j`j=6M$a;T)e z>}=BaKuwrkQSc<5G0_Qd86Syz>xP<9fEcEVPI^JRA`0Gy2-$ zhbrdOp45Gz`1x@KlNiWSSh|7(4?NosH_~Fi4j=!s^i*u(vQ^Mg?j~-u&+p9L6okuP z@bpISrk7=aQ|f||c;Rk4ncC2OUV)<$qUW+jJM^Cfd7vF)La&Rd=s(a1h~ zFie~&fJ(|0DQJ-8*h4^%#c*>B3#U&yNMqaZ3*7yJ^bI|vnKg?cqa2Dqp zk*|QfU8!3nKJw&dX*%@mbD_wVM={djjLqw&W7H1xtX@}inv7xepVoMQ-cWbw z_K?ClD;-0VFKf|51TZP8a9_ajl~?KDo5WjXTSXWPD+E9pZr;b@!K){x5i%djsH78A#Je?6^!-=``5dK3G0<)z!+(>kP4-fp(SZHHgt*t;;z4Aobr3cPI$Mab9y z#e+ct-nyhI$?_+A+O~zQ;dcK$maQw!R-(%msxmEH`u0tJ?c0jTIIktxA6l=70e{tIeojKJ6H#YWCPNbeXi|E|oOOlMM ze^PMpbJqfk&NCQ?Qxz#DEHyur8v-7~%FoEnoGoSaRx7>~EKEl)*^5V{$7x0g#XD{r z=)B(WA{@icStz-~GmFqz^NzgUho^n@QvnvUK~NPWMx~S|kF_rC7|l?8S9y}_!jLCT z0DNYhDy*vp!R@5z6R;|(je=GB7ywikc>|jaB`66$dS4L=Sqq-h0E#e*Px(-I5kq0X z2?X~0Vl7;U#TdouYGeNn;ueGVR6AU^D7@BAD0E?mGK5eYi4P>O`r;z$@PQoJl5*N@ z*?t*b5#jC|C>Xu5v7u*CGUb>{_2h8e#B+%>xTHE#mYMBUu6SELm5&E8h4EuWZZl`5oL;bb z+4}ADU#Xo&hU)J!j_=i*^K`}O_la`q`n;GxNMd;Th%&+Vndcz)fqT%C`M?}t4MLTw zWI1h6Ued-IT{KN_?ZXDw1IF-Al~6ae`vx0030i!@-w{B@lKfS9VirWyX^84=bYzzaw*ZT( zf1h6)=oFdgA+q^^sBsPNYqeqgia*En`nJFU{En%J&07#R$WC?kHt8VRL`ds?G%;Nk zq(dy48is!nY}bjaw7eoN_-FA>X8)1*pU{9ymh{n>O9sN@okm2I_U)ICqp5d<^yF=d z3^{o>#f9Ek>+3^%^>HIFn%gNqVB!sPM##mlDM@e71(vAj5okXj2^dbfSnBv#*_e_1 zZ6dJdYD*ErJ4xq+OW*&VwgD-CGUONdy+(9M`t5p;|4jx{a9w@wHZz}p$7A4_=0~&G ztn~mVk~D4WBxbvIa_fO-#QF+_LAPjnA2Op&YZ7d|;$l6M$aOp9{MvSzvnP9{b@E0E z0u0E9iVTS8mjZQn`S%CsH}4sBl`~?fLfsMvNU0bM11jo`uj+H_+nSAO=&e+OHesNJ zs8rkGoN_{!{YS}zN_&h2vOH5iw>eru%p=>!tLC1b0!wL|ssach&08M z`>V*Cbi0?tdLLpZ#{6VW+V5+O*kyMI%LHBy zEDB>#ppn(}_!Z&&xKS(}kUV;dDeK~;4QOQ&y9GY0=V2#FEG*&_h!qj5=RxSdmIg)z zZxnM9E9SxB-M9)Ony-Jx?l7JwLF&*_kD<)jvz6PW=X-JNL02s@tcy>Qyjhz9HO2TC zfBvDf%pCqJUU;b=aAuU=c5QQ8*aOBq=R-H0r4Jbp2N&gdMX}ZilIH7H_ux-G+cRgF zb*wZZ$ZM4pOXH3K?X+LKyZfvBSaj6g9oqPU>}dmkvGQ)?RweU&8K8ea>dN;6)@Sk< z2ncm3^fh#*_#rMDtCLsxGZb#i(8v@}{IW77x%)Ao@w7_HUwcM~Ztky&76*wusQn={ zz3nVav*5QXipB=AVsK;35C(?PZg^+rMo1xvIzIjGEuecNdrnT<7su}Vw~*ZGOAWKP zhsNQhX(3V*(D~luMdh{?(rO?+99)(3I?f_BlbRvf7pK^LvLNFFyNI{$<90!AW!tm{`+YE!ukEJ*h_$WC%~4_y78Cxk<=a4tOAz6h0Yq9;7~OK%7*yn z1CW~IXdz2(Jhe8~G7!oW^bk@1jIN^Se|?po8xdrMlkfXB`Z@#EZCreGo%d@T0A#GI zQd(gl3@<3x3Ck$PA{2_nq#gOvcdXV7Jb56)RbNQMkrJ#S@GMi#DZp(APd_A2`LiFA zRjH?94EZKJH8qNVM6`wT5xz9H!ZwWsh|$E!`)H@CpE~DDD1*a~;4+_^4`r_&PRSKt z>W%S8I6Eg~H`i;aWUlzd?&5sM{PTR_!v;TOr6RFIfVGD(SR75JRXs}c$G88i>3}m` zZCc=s2yHgQO;#v${v&YiEn@^ywQEkc2e`vN#JJ(0I|nENZUFSJjg7n*DxUya>ewgR zKt-ZzeV~yAyvO9HHktUViHOYQkh<-b>&WRld=RUNlaNG=(L*^|=9@m*4%8KcTA#G_nk6nzoBicZ{q(VK(csTv!{6qB#i z;W67M%1X(a=et>P@W0p9q)*&Uz+Sd=wfS@bop#`0c36bBN7SSaW5zM1!npDRji3gh zah`kiV+14NPwaYO&7yy8OY@Vz&JHNta$bFmm**GZZT#O9qiH_AO|VwlEv z5|=v{+DktaTRNYu@;HO5XkIP-fJA+W397jCs^AYqmYF^x|9dwk9yITr9)6Z#*m2ir z*fFq407Q{sYOe5ib`n59*2^&mSDV%L6g&Z^8Spow21(rtdg(OVDiEDqsZ?pi3xBpB0b7NByVai(rK_0d;hq|XGvrtnu$^f5<$RF;hy)K8NhF|h+~&}CuS3(FYCB#46@(<0^$IcDb-!;)g=Ykl zKY2X8UM=pOScDdEeqV;f%wd}la1LDjk~sLLs(!u>MSlHd$7Uq4*zm3AIt*oR^%_{4 zQKK%^B9yNuqH@<|v-Hq^79zb~Y8JIVG-}lO{G{62tlte{9|LQgVviB-x~RZN-(FK5g=)&YyUz1b_(!xGc5PaAy+(u5e3t>M z_&G_kdC1h0Y$n?O2VN*YUq&ePNgKJ`FRQ7YRg2mGxhm%KL9HtT^^nJ^H)-YFFsF#4SM3$>t?m87xj~?g5LO+uA11>R_LiW47$Bl(Ek`=!O?(B9G_S$K~FKO@_HY5^Qk0dJ@DLphi0?@3^xx9tNB#fP^A&L5%tyy8Wn_r z-*1lh$Z7XXCAA7k*2I#~>ja3*9|NJp)Mx;5U<^R;ZRL2Qp&PYS`(gs_;{&vL;qG5n z(uvgnt<&$d-msS~KYaI+yXJsg8-5G?%QJbl?)0kl9H$KxP!oAo5WtEm-4a?TK0QC_a)1wbT+kxaxq-_sve%FryS4IcxQSw6 zZGZ0E=}zqN4+)_OAqPo;5h{xLFOy&6jNi=|8sNwu~UZX@*fU%y__t z^|24-OE%g^WfX`ypQIxmk2Nk<4T~1mW~3 zyP>+^=!lWu`9RP{Ao5`5v-+P$0j*=w4*mzCVCCL&n_bzT9ZU0sIRjpw>&G z8U;$ebFrV+qPhHsXdysPNlvDl*FyuKWZiOgT9bknBrQL_4vhDd5)#t-s7MPId*1F+Y8C762?vNttKmoW7+7ya{vv+V zLK$tW=bx-wI$8QUIbzQo7e(}=v3$x7k8^F`0ui}bM!>O5jn}h^gQQ#KO#r^mU+mT` zI*+C*6aX;>o2Dkag%}$f2^bk+Eap}}^iSie`L6O$ZYV}FDvnT3 zCpPS)#Xs-iv}#5+Zvm&*TGzH@gzqdS^@V2FL=u1JG8H;&Qfho~hUxh;r}aa8_O zg`cI}MigBOu8(80Aeq^y>`ZqvNw#^eOGMvXny_fg1Tz+G1Q`y|2ijj@l!}~5Q4=Ts z=II-8GJo_PK83fRp2qlnH*5{-46W=`(rAUOV8UZDys3HCTIueEB>85wL1A0n99(6y z<5lwW2<;Ku3fZ7dzriq&&5sZ>X%Mh-)y}@+6Y@s$^Fzh&7abP;(dF)>=%p^z8*z)5 z9%2Oc<+WQx3z%C=!xDKZB_Yh6?|ujQu=(6eX&iO%;LKN_uFSDlW4jQU&}YaHDJo#+ zOJQ;V0kophm_zjF;n01)AYAXP%t#s2POE*0Pi8H!0k>X|b zh42RbLjnk4Zh{t(HDbE5;;Y-rsEUIWq(5KXiRnl_K8#B<-6sFhkn?t7|CBm8tteQ> z)()i>)99d}@06```Z9}0CdP`8A8SpFJ$GGt#6q>S?K5Aw@mox^o>r!CG*x#P(9N{8 zJp^GRLt+O06WenqjM=(g()HdN1etdJK^@jP`!iJ?iTej_zp+-Vw1g&wiud2W!&f#X z(Eg7Qv#4m9wd%-0=g6{*hZ@LKW7C!8`6Hvinyni|kU2*D$WOO&)xqIHsqmf5w=K!g z^D*Dn9%e6L7C7w>I6tzLmbiW8Mgk1j8n;M#jiWZ6!JsR)_a_cOXHd~qHM>1Z|Ly#z zN_y0cSp9CGmGDQNwRSp=*bW_4_km^6oi=aTMdWD(xQ_`Ju7|srjiX%_u8SKT{!`T> zD7NFQ9ZGY}bl`=Mir!(X-Vv_aDn@~2Xf>3HWx*NjpX2Zh`f}hSLjW*2T zeZm+?l#^g@V|@C1A_67<>|~^;6=gg!5I%;%r0l&xZ9b(Af!;>cVDT-i2C0GrpcIH% zFtT`W&_=yUVT3Ymg>%rFDDUQ!5c=e^{K)Rmc52L8OhoZT`m$A8{{~fEYXC5xX~YaB zY1c+M!D&UE;84q~j1t%QU4}RqSRl%n( z^7559h1WW38L7ykq+H49-v>R=Iz5`mSz`OU3k6flclODqbmEq?R5WGQKUh2*%Hr+Q zbVMdNnC$cBm$L>F$JF;`DT{8Wf2dz2BA$+ewfv7W#djk=L;iw*wCX;(AlSX)QnSC0 z(iTlDT*xy=0=Gp@lAtquPm$BY`9cqyI3ha7p;lXru=6ux(+|Cv=|!}UVFcrME6Zs5 z1*vb|RWL21&3X_j4N-?+kpm_t%ll}0Wf$nv>4V-@r!%xrf#a}|C;2p?tLbL-ySGa& z7hAVU8Dn8Fj0wW@+1KVN>a|*5iI|Y%naQ5`p5$l%7q5%lKIaF>U4JUW6vub;@*Wz< z!Ab4&4>yHlngA>gcGEe%neT^TvW56YsC_&y4lbcLVM3h*dDNGp)i*2 z+_wnEZH#Ll`sDM$l5fW#Dyp2IxfM6{8w#rVLR3@VOXP4E;*5dpw&BkB&qm1l)4sNA zEX6lMUiij#iowfMdp(+YMXni;;4X!!?A3eE%+~!szigh5%$L(C!UPa+WI=!$#vP3| z{2AAFz0uNbkD}l>b4`$H3J<)@)A`=>|Gz1DrPmXU7RUQijy*YJxG(?)wYy?yPFjYi z+J*+bn1UCBmt1SN7fJA4C3G)XDZVG%c_wav5X;1ky?z&Ln{`q4yW({s=)m@5Uz#r=9tzTbV=#DlZGpHk%m^Ok2tGn zXkr6s$>FwNdV@0xof7H(JRk;?xe7egK$5RPX70MSNFlMgq*f#`7hjn{RuTktcln@=QbS7}B8kiVY9 zHqNi4360lsCEfK^b05AZ2?UbiuV-AR-~gP8g*&GKawbPZU3oAB2rOc(&|xfZ{(h{W-~W5G0kjmFU{*5>BLmaDb|kJ|}x03p0ZtJ$CToS0{2tN;iPmtxNQ0 znI2k{tQfU}SL(N`x1DXRO!N{R=)~7z)J;!MXvBf$i>^T^DX=_L5dJ0&s31o7bRVI5 zz^Myixin|xZO`TXoXVb=xc({L zn&!wlC)91}S$+PN78ERs1pv$A=^}acYB`2u-nYAnn@fIV9WA=c%G&w8YkbJZw`~QM z+899ED;r@`v$vL&%>nid3LS*BH|+3P0j)KW5Z!+z7I5xr<@uIir=;4WS60Ui7@zzU zF>~&s$VNl37q7qjrc$LUY&%oHQ-z&hk?u-UC2Z}q$X8UFJIzRYj+m?W!?`jnjTmCzo*tn@+P}u?TooWO- z&7q;)VFSJ7`m{t8RC@K%0uAj>3|Jb|IO^*MrK!?3?mnjisAwNBK(e{Ou*9gZ`_+qX z-mW{s|KdV!SYbl*fhI$f2gPbGY3c?jP&P`wWP_W;? zKC8dG`5plqdrnY%C0pZ8t_`MfUz0!4kw>-Re z<@iyo1)mfk0cz)kOn#x1nU?-f7CIahj1zvlz0VFW~a7) zd@!*do3+R~r?m)v{gpE(arw{Z(A%dhuazebs)e=Wu8~{=isBb2O}Gx%q8|xt!4^?2 z;us}3mP=@lE5C#D1=N%uNx%0m#J%Y3VSDyEAq5PPF2qNIMY@%ia`PcTCUQqEcBdoTZ;Mn*$aLV8xV}s$e>C{bim(> zhk+%sc3R;&0}QF6%u6hb`6P$AnI%Y;!Uq7C$*ioW@zvdN6N>7elh3~U;i3Q)QglV8 z3ap1$A#Y8NgB4BOf+#X><>r^?@*N0EqnklWGsG{n^nb<%Fn~y@&>q$>LnF3fu%(8! zyu)F|CZ>cvPm&xk-zrE1`lpzUP)O~`?@-%J`p(1t(TB#Rf>4e-fz=06nVZ-{vMW@_ zMruzLW$nWvh6m&wy!y3>8-M7!hn03H$x^M{Smb*#h3fcCGk8fO@}w+bVm-#Z-Bo>C zUonyAt9M^tGv2kL-r`&vK@y^({dI4m>~VneDSKr-08=Y;Pdyf5#C<@9T)rW4)T&>` zfg?9|EZf(ATDG$pS}v}uSFBD{wce+hVN4mf^I4AH18GV&7pP=Ku$to-yKJIz6aV}X zO0e&2{6PLOxgQWceof53b|a+&-iK?XgxpV=?2J)keGJ#N|LKXMp3CcwdPA|3;vu*G zj;+rYZ~??dut{Dzn#;_ZU}Nv+*4w_4Y4p6Yp~=}Y7-JzsG(9cKnYG+D)S&r$?3ZvQ zNsLc8iUd&1|6jV~Kky-2XGk=zv=Mm|DYLJ2A^6^0Z^fONqg0GsimAQI4C2ZgF4`Mh z62JZ#UGoVels+@J>Q$4DnpZ3VaR6#20ocH&lHLwnKD-U0FxU=%t8UCyh(SF;q8+Mc zDB?CMlz&v@6|yCi?zS_b#x`--$O?BiTLx-VMsZdmoGt2ej$f>S6JyiV7_(bggkCxr z(7rkoQBw%GM0+LqV2KJp`JopTc#iS%hDdWMok+v~PkY492nVeo#{6|6h9W<_{Dpux zDDF?o!4c`ar{cSkRi1~dU}KZ9b_;N=8VKnA{!DDAAa)IH2KE^~l$`r@Xa*I%?PntP z&hrX^0{|k9AyF_KAXXLGZU9K$<5#68zdsKD82VS<`mF@53*rdQlm~IQD5OI@p*6No zJijhqeQVf--MYtA*sYllB9mg=6u4g83tT=pjlSubX)~An#S~>X46e9G?KiIUEwJ$J zQtH<3Fb3raq3xF*eAAnCg}*`G*P!bPx$#&HRmOwXHI?!S4XUyJH?SJR#@~1SZ$$tq znq0>VHj<}E=SdSV8uHA}wxGc{+G-*=;ZRxnxPXEtBxsmE2+*ngQ8E0$*QklMH2u-$ z7twC9H6|H7%k-_bQnmLpK=Of-2qSaeV1E|$<$Y*r6KNT$d}>!#7}czxbr=xcH8N*R zP^_8Gk27bWLkPk^6^izw8qi}~44@cXoBjIqaZIDX%>s;eE#=Vm;5WE-AL}ZcLfQ}6 zF&>1KyXyaFjw|ed^#8R?GzE!1R%4vHS_CX`_hF-To6@N~vj|uH_7b&*eZWCEPTuIG zN?n2v_U)h+YvJ4{u@f!u2W)TUTAgf)%Zn>S7~x`WMNGB7F#MJn;3JMefCHIIgRzBa z)5%7IiD~{-rI1c_exC}=x21kx?X}(x;`lg2`wF;&{)U^suSDn>&-C5EO|p6g4UUs? z!rMs*DJL>;vET0ZoL#q2f>Ib?sX{DNzw!cDolNg{jalEMP7s2-+c|+OQJgC9w5ujX zv69z4+(55JFG>uHmR>vZs^3mam1NP!e6u9?4Zy#07eirJzj_n)Tk00C7}WB>1*GMX zF8Jwr5^?#`-M!c;jnB86`=)O8+Eg!7=IlA(ib$s)jOxxwkwl@!m8g>3TMG$xCQ`V_D@sx2w~q=Z%V(T(8z9J!?_ zWb84E!)MZZX3M>NlZBcpXiw%nQDZweU$Kwy*sa|bqZ?@#a9z^bLwN<_=DkGiOm6Ll z{~6L9*kS2p{hl`Qe;7jsN5Sp#HH@>AA$iI#XsTDri)v#PqQz&4al^u%&ARGGQ4P+0JO?TNN%ad#pI-#c%ThLFnee`pNp9Qf6LR--6|phQ>e5;5t7F==ZTxUsz3 z%=ztlpH#Q<$g+w7tgnt~9&v!y3vq$rlJzH@!yqLqDCKYm6GEbRUW)1t@3eD*>fhSiJQJ|m|tz*qXFqA{CC0%|4 zs>(O^IF@AUY)kFwxB|;7DVzItfFT&b`04tw=HAg*8K35eW~TVhsk{u6)XzG7|Nf*X zMc1tdjzB5uVRm^b`BD=fRmO9MFLW!G^-Iznb)2Cy|L~va{WS}INyHv00Bj2A67K;R z*x5D(M`RN@X=Ey~WD^IUBn4hA_SA;{rzq$|!=>_2OOmC)-5l;C+B+*RSQ)8!bjp4@ zMxS`_{9r#4uj|=wGOt-;Uo3CUjEPA4XfgA8j&za$lmk**i8lO^2tmF3bsLq2J4(eH zHGyX)@&IyfR%Nyc(*1Z!vbUgO7>!~jr)63t5 z;U*R@c|%YB!Wf&UDu}h!MWUr3z;5SGYe@fk#q~DfC$}q&G!P}5-;wz%{}b{`wB4}7 zg`3}VGnB|LhF~)J8ehF%FU^Vh<97iScxPV&D40pR1%_OKVM*b;lLRfoeL1YhGX}B4S&r7tk!YahyMK^Az z|8Bh1$Num7fzZceY4$9!(yh69Rw8i)`t0iZ%<-R$Hl5LbGEWuW1aAhBN09HZ?W6YtJ`EQF__%Llyb2wlfzkhs@T!XG(q~)I_{8V5w zDDaKuRR>F?DGC$x7ayyA%>JV7INsaCibqtHucGp$G&N$BY8Y_4R_KfAB~46BjN2CX z)tSA&ZxvaYI6dTZmC(jNVBH3fyD4o5&_=pwdl%+@3}U4Qv2r20YqGol`}9VzaiALq zNFbCAB-BI4nA{*i9G=539gpBanCI55(>T(<`~mT)I`ECurPRvA%})?qB!R)<11Gbj z3lzj&l*? zUfzMk>-BWSQto=|gIuhQ{*7#-gnHw++5vLbu=$nU|fMA5(m15J? zC)(})S#&D!(I;jxK8O`VXS*p$G(yl?P_RvxZrE%p9zrb5A>3)>WOdC8hTy5r3?ty# zet=hwWg1B=CO+-MqcQmY-?VM<1L5%m7rNcC$Nr0?sZ`55I~2JIW7+z2fK<@ddoz9K zBuB1hVU{KH&^73-X%}7VN1w0<#HlahbYjbQ?XDL(TRb^xq%0X_H&Ivx|;xp2R_C!at@@cz16RGQ=3>)IbpE z&>9zAcA8F*=;5a{CTu+NE?zDR__8bfE}tAgl%;7a0oSWGI4;tmKQ!Xf{D($VI|sen zgQxU|(9pQm#IJs2&Tq%0yGAk;bxM2VYBLG873?(}V7~^>@LCooA3wB8=l(n3^Fh%7 zC2?B3F-9mrg%@dHmaX|u-M=Wfr=cHeyMYd&YNvF4n- zwIr|yw>}72Bi(Pa8$&5}yn}v=(>lDv%um%uJ5Ni%^wxwC`qQINhwZ?kiyPQtE3_2; zQr~X(YV7EXHeXu2PXdglYQPGXa#^fK?C<&F%iSFJi<%b@L zkPXP`5+h!%*L(0rbPS;3157jc&>&w>o-`nn>=Dw<@Cp!QBn$#ksG?mzV;aX6fv4Mj zgJ&KPIz+6emH3$(Ak1zAw;GPxjj^}U4_@3q*}vVN`tNj-5%@`ON14$t&)#clk<`$y z@Z%D;f6&5=I8LDwHv?X$<^-YmqQ%0s_y+wMLjUyOerUCMLWW_@Adzm;sjt6xhvoNL zE*$pIA`ig`!Mo2no{quUuvRZU{jb?j@(l2Di+N~wOq|uPItR=x4!cjXR-OYCoam&} z3xu_Up>~dTdu|P;CV4kz__Z~8(KHTJi;NXf69n6mf@r$^HkI`Fr?+4G4Rl=YbgJyzhERD5{!Q`%g&J)65Dl6g`d)p{cOOBgE!sLM<3QqnTm-3;Y}rSi@wCnkdvzZ<}jGGp}SEW zmn(G|#+LRj@bLi%9SbK2eDRM`fp;+RIEh$>KHN`IjnbWO$qS$MudvCPA)T-gx`9-eCD=a~~Zv7~LOiFKcS zd?BW1#6FwG8&i^M>?TuJt$K9c^6hZ3pUvl#73C)m>xoy&40V0Ci{5tO99zaLZf~rG z1tA3Tm`-X&qR%)0^w2O4yqWrBCNEAXZmX`pTXz<+81^P#6}$LKD|qd_A#oB1(?J#|KuKl7NJz-9J^WS*hs&E-5RiQ~neIFnzH z0+QU^%}=4?m~VizMHG4rZbZL?*r0A23Nq1Y?A=H}f$Ts)a9OBSCx}v8YL(=ZlYhA# zF|2c)P)tbjCvU@Fuy!`-qD!go=6`!FkH&l9U;2uPaP1(@gi#A&OX^uMWIbOGh$V7T zNylPpspd7JnXPuNUE5%nt~Qk_;JIIY#rdcR+jqP1-(Ixm6;nOn>%p}7;e+ec|KwvY zw`9aNJO9>nqYGQy{cX5yM6FBr#W)uUI{VrLYiFnk2z#Nzpj+@;7_j_Q`NqTM-&Zbg zivOS0ubBy%z3+l8Kb{11fS0xpS);_xO|Tk}Ju325LYR>W9e{h6cdJV1p8c#CO*oaS zvbxK?>!)NxpjeD_T&t|brrBR@@l zzblT-)9tzVI;la%*};xe8|~#g@mQN2z;3~KDV;(Z`_7NE?za>-LjY4kbN6`8_0p=) z;E^;-RnB4FMb<~+xhRTZ{<`{&V|OEupaUxv5eE?fDk5*^n|%J&6D;w@Y{9K_ z0#AD6r#~3zFZi)MP8Wf~Q|iYo_^I`O5Lp&)$YbzT%hxwM6#R73fg|iZyht?$jQh`6 zbYp&}xlnHm9TcitiVL8p@KG2}a=DxF98*`Zy!mO9>gs3*#Y)3(!4=!Q9gN@2tIa5! ztQW)|`?&Z@WbdSfFY%X2I@J=EXXMORr^Xqw^_OGFRUVS%5OW1g0|8KvB*OLwZremS z2->$%S`kKIC(_ZOL4DE6eHKK{Gq;4SN)$NN2!LOvTc%s968w>r zdj^(maaJVBCSnFeIdBtoZfe}qiVYt0SV=%2up{QWCBgw*_GAz>vZ+z;AhB>jBGC5e z@HO;Rz0F^`q?6&`jR8JYp83Tx{+aKGgJx~D`!~gKL1ljAD>FE#UlVmcDU!R~PP+@p z0^7jPlB%5?s@}9TfmS~#P7o^;B3*PVbhtw)tJl~dy9`8SG_;xkJk6bgT5eo@Y;hth zR#t5A&-XBYTOFZq^{a;SN!&%=(TV ziARB|!MMAroPg=aa@ z?sZC@f6|s`@bV9ZawvI@kT3U7L(*fGyNp9bABLbDf)?ki3-lkS8DKhE$Ap3M1Ya=Z zj?;S1EG6t6)ene(Xq0Oy!Acag2?eI_v-W9?+VCmRL|_-KFJvYYR_Wx#n_-eVelNsX zK&V;FW_WXNU+Jj?l!6fi{nSr{3F9L2M&nSPROAyBOFy1evBieh3eSaBln4{r&R?8> zkttBGuO2X0&24b01ew4hZk^%C7~%IkRA$h8!L%Sp{UZVl@ms%8bKy5_xW2O-*Ks5$ zsM)|r5vNYAyvBFDFI+{I`@iZ_;A42_&c#u$xxKV!s8F#c3f|tUoki;q}L+dz3a zmgEFhXOf!>K@=)NMVAw^+YF`1a;8v<=FI-!`=ZLF{P}h0A3W&W9n0e7&<7$w(p?M8 zL)p_ptJHxEEy08~_j~gozB3kt>&F4&P!{}re>Eg_O!iZ=q~!V1_TS(YgC{Zo4nc8y zOa({5>%NG^*KhYv`Mu7A$is+gk(wRAFzAJ44KeVpl1KnREH_XqxkmGSmxqfpF#&L9 zlK7(<4>~GwIW2M2#59c*&~zgPRAl_U8|##j^8}@yBm#&6uGOrrgr4n@3tAlo>Zcqz zuE)qJ5|f|b<3*#Il(bUWTi@xR7z=Y5{;HT^0sMRC*}JR#br)iuR|}e->Os;R__=4$ zg-R!T23nX|2JXsuF>$dkL*1%CrnxJ&!E4?!Z~H-9J^3R_ljI{cO9$S+C{ju1$fL)tyLY57OiZDe zd%(*yed$N1!2dFXuBB*EL_jI*yL*Sj*FBcS`9+^d6#Ok-_2+_qSky24TLYwo>%>SY4C!YV_f)I>CRZej0Ehu4>^rb1wnFQvI09vxY_A7NO7%oeknv2dF~WdSEgP}w2)~64Iz>z*I)O98VxV&l(w%APy^vu>WtW3z z%I#X(v!N6fD<9SaQzGQRU~n*E&Sl{Rb_kYuP>T@>dML;~oqjWCT~@(*)770iY)tQM z%a|saHk{Pwl}X=SU2*?Au**Lx@zYK$5;I9RHvF>7rNRZ@qja=MrDK_;_ZOj;2KfX? zX-5|Bh2N)+_OZ4V=q#$-qJ4yyNH!AH=ldkohdzdkBQa=R%e`HRR=Lv=i2w!y6Rrt# z6~#%|pDCYly6a@cX35`#^<1t^OjAdHP2<0#P^v#wO9>zYHb!Ct7hh4_~V)z50%pbuohigmN7p>ojgjIY^ zNx$!|Z(XnvE~g%pQPvSz-!5xqFm`Gy6P$M5|2sl@E;Se~WVDIGxJaf-dbWi{>#uX> z$p?)YqCS${xNSQHuNbx(J@T(%1qo_n6sK^OZ%y;iQ;&N2U`wk-3Mq|G%JA_T!g4kH zm7T!XVTp7YRc+Vv{5WAhn2?mwsjyHTjWL0KU2f@r1N9DRJaZTO0reeUXTGWr7|c)c z(LYRfMdd$;n8aF@Z1K;qr%+KThT+4^%+NlKZzD<;X8ku}5u&N*3V|mcgINKMuy1?! zop{In9FJd6cyyz`nDh1Gb$4M26B5l~9Wof9GL~@U^;gBv{AYBP6?0?u%RzX5@7fd( zq1~^orRUAsDgZh|JEkBWIt2qPq&=o5l!cg;YPxTF)|r}N*$-36Tetkwi{tSl3QW9b zy)S170GXC=0d9$Gd+7tR<9;-PZL~K)R~2g9jd=jHma#qJiSd1Q>`OV;&Ykl+HlL&Lrm8FTjmiH1 z1DN-@#N$1*asxTzOM0~eLiXWdT)%!~>c6qIU#vjC`xq0swSb+}>O%pl*3h)|z3$#F z4_=ua&XKcgsH?j-mb;XGFgL+N5CXmmV&@4erwJm@p@bq=k$Gdp&!aa| z01W(M#3SZt<;?(0xbpYDK#pZzu)E>HLGv0G8(2a0Q3jpqOxab&G6v zXEXmAYM=i1QrNHaT2mzFA!MZlE^5)_SV1J{r6ZDcQzArn{*nL5jA>%hN_vhGKX%pY zYA$W%CN9r>{tHjIPPjv~FwQVj)mcluObpv8F`%_Qi{Zv*6!$E)C#&>b&e-i9ZAb|u z7k?Cg@9scHos^nCm0_=~2!9|*^HmW?#z=!f)IbTYet3-w#jjNEm%V>;^?~sZeM98) z^K6BD-( z5Tk;N=!5KyY}zlmqNj{!QV6tzF!MqXgd6dt3l0X>;xea0pl^9AeZ`0Syw<53B%f?+ z+RnNE{~r7MEcI{8dvv%N)+%Y5x-A*g$myjqO1Oe0h>`Yv#72@ZfqDJAU)HEX4*=ly z_&>o?ekqm4vf$jJ0pM+746XMn^Z&i6MXR-4`29|!^6KIjntFZss?slcD@qG=MO?x!gD)$G<@|%rD9?ZdXR1CXKwqQRi0j_ z+?04z1_@VtJ;d+#7!7LFP(!+?wW&GNOI2czR9sdBX z#s_g+x%?A^ID1B|n?aMqj$B;+&CMqVO2mpHk zFk(vvxknMn7ZYlO*~_WT%}Ey*(!(X`D<6FMG0T6eF!?+YUeui3xOm@)8d}v(aVt2~ zYtGH@{!PK2{!P@b!mea%IEIZ&ZL?E~06s9=x;(>zExkUPP1(rU?Gf}K=YZtvJAX-V zuev?~!8_{jWqA%I^HW#gvpXXW|K%+{U1#|1&M2~~ghh7;1Fia*#LS;XEX6gu z&ni#HY6xbo`@dKy-({9R_A{P=v{q|DviW!4QWxIx@yw{w^fJ`1q&@y(CZoIV%qJV% z8dJYq#(F(aGbfKpaLLVW_LTJdM<5;Tqw)~G(OIt4QvqAN9QQ=`I5OH*^Y^+=oQo$7 zA)do7UrAK$Bqotk_tsB)T=yjDUug^DJ$U=-p1rtzJ*NiK_l|`-(nT0FY3GS;4-1|O zK9_Baa;SV|Y1GK+a$1+6@W*Dm%50TWhmzfU6S^h~wf?jW)&rR7SSY{4i={&nr!d=5 z0FKSou|}oR<)BiZ-z3_JUR?{dw=c;MYcHBwG*ndkT1$bJkE0$O*Kxv5<2c_qaDk@0T0HgBg{myk7M!tA zF+7xcEyc?#_EYAx0>=I^-e$J2=_H}}-t^Y9OdWBN!inuboxjKZJKN9m&0Z8TsWR(F zdxJLFpqGzZ?J)H(^l+o+!>|gve?uCtszz}RD!n~8COcME=XNA?Z%!&y>J76Hf5q5I zu*xTQ32Y*|!FG&{atWU;8Lzb! zE1&iytJ-h^1H<9dr#p3HP!x=aMBy??<+H+<@rg_l>HDT@X2EkjcdM$)sc3qQ5sNK6 zDR`fn7RfY&}7@cB6h{w*Gn173XxSqA06n^LI48cUn@})!lw7>+bn~e&L5AV;RtU^1vT@lg917YkC^LF!q!fJXdMnc9E z@(+7qCw?d6`(F`7VLw)}1Q8)mvo_Vc4#$jNRYTO}_A{%QxvsM|yVz*`EBpk1tL`Lc z9-xIO@8C~KYOGc8zP19Bip)TLY8N;&qXh%vG&n`kc2{%{IymV+2Qg$`?IaDs1cD(; zw$HHVA$2EQ2Mc(|sXZU|DP zZm@F9oB~SNd7Ol@$e&ie15bd0)I=Gb0cl|nZgO_IKe4DC4{69xh)3e5+H4cB)*x zv8Dd`L@RuD|3l;8HwwP+@M4h8&%ml>bmJ$|mFs{k5T3+AoKPh69eulR+Rj^UyVk$Q zQDQ!OMLlzt3u9HGma}nvN7g8oY+#3PyTR|xCm>G_RGnHU1p}m5K5IO((eleT^95R#aAnjPLLJ91?d2p2fu?G{em?Z1V?8yC ziM%s2N<~!8fuo}8b(UvHGH4^SPJ!j+N``NfI@MR5RKOIvTTc~X8|&uCVIgNn$2ZH3#rvBN&_Z!L8Bp-Bv&fQ(?V@PH zr#PhrN&?5P$<#Ji0X)ZNBmO?MDrqbfk?H++~OX zux?t0a}VlxS2G>}?(8I2E;1)_egcLxsW9)qYMBnjf^a5KfZl*GIodd8in~WjQK#E> zI8`_KZ+yHQ3^ywKN0iP~nC^T&WF;c{&0S^T8=488nm$Y6baJn>>np$cw(|;+25kYE zPgc9cW{jqzqmmUhPBu#d{yEG{tJUN>1aJWEB*=S@6~=?`aZ+37QZr<1$5C}R?NW2==51o`{NuSvXZqT$I`ZMh zSmi%Xb}`{xYSZ`KDbw#P#rM(__{;v3Z&LqW({_}8hXRW~F6wIBtyuJ21ey9DKmDv* zyN~%j8mzth@3Wg;2W`@# z(!T$`YP%5IYrlC<-F)Y=a8qRwZG6EjhymXl+!1~DY08VnQlt@C?2Y=CTVfrhZ(&Bn zGs~EX|GBuNVILszq(>_TZhbKWfc3$LFX~$Kw4VXMs6dCw9?mkqPw?Pfx%3nh3IPEe zfbt;4!TWM=Fs7goC42*N$v%!2@QFNQ{%rWK`|FZ7WxxFytpIHDI7%3XW^G%dJT`U) zdG48_eIuFwV)KH!lwv)%v)D!x*;_@!^^fW13uZn(0pE_puBMz#neX2t@YbsHuh28I!k4tvT*HAOdGck^<5kSA2Gy* z6uV@aEl8q|H1-c<*n>7!p4((m^*n|a$yj{dd=Sv;Cv|4~$H%?$)nkD$#zN6J-4)Q2D_0AMh5 zja(J{CxcE4C{Z7)N{GTq*1JgT4c7d880P36PV)OfW( zVWXGF!JEep7Ek+a7861iL03=h=PP%2lb&~m9)bFpM-NexPlU3W_mPzBAs4KZm-8j; zAx9W439!MfY{Zi#$uLPVl?a0airl}fc-H5ZkTb^xUI3yTHcYUNBreL4iy1dS zz~dh~`$zO2D)E#QNL+ZHN>W~%Fx}rOQ73M9YD;g|D1=!%Nj|^Gt9r5scmmPcW!PdB z)b1ZW_y2OjFn~@X9~z-^9dWgqfWAwh9>zwuAt(2XHs#8EHQ+etjbVM}jD8Po+f2aX zmpWaVNCUS`)(vthxEnwsP3A>^I`kW zu3wAS>5nh|qhTo!-^YpGS(o)#mA)#DTa9;SDacCiAGl&HJn3+pZTl_zY%=mQ_ccw; zEyIvO8DOBG*gM3@Wx9Fw>$uSI#&bINZs?xkWArxPyM)GjGIAVpfZT$LAObUah`M<& z-Sv?e=ig<8tcqXIe9G2wJ*PmxYaM}C-nl9^);|>fsc70|m+9Aj6vTPT<_~BP#-?F< zj+HZ%mHhE}5o1ouso%SI6HhL8h_zSnG|5#7fh&n)^5azE481bss<>LsiWZIXf4rqV zjh;VRaDN3i;W9~z)f%=={J8mj^9?-kNA=Z*kyny_*4Onq*qd{f{v+J|Fx+L1cTI7Y z&z@4d6-98{p58fW-&?rE%m4$gapm%}A5-1kNG;p9yECt3#YejYGipn2gN!xRp16C? zoc@%Q@3h@8;bM}+zWkc?>bONpo^9VnZ;}xZ`tW>WO&*B%+ zI(UJ|SfT%s$m@}_`|X_kyj9$hQ+-(Wq=7AUWjiu?@W%=nSsaG`Q4|Zy>**S2TnjzK z>l><>>b0y76unf&;h!`Gt@jx`!zg$=&;^bokHWW~Q{)!*az2zwK5?rk4xvgE1|$-3 z%`lW&Mr{A^%Rm7#vuRgqe2f1dDN$E-$Y>ze4c_NO+~os-;!TEy9KBdRoWRlVAHJ=d z?0c&6KLe5(Dm!=)BZ5rNhvwzt?Q_Z{8$*syABm-$^AJuSh8m{5N;f^UAk@xDAdljg zWhd(a{dUbysYrvnv8s+sP*2*w^VFYHrRm@kyp9m6_}uNqR^a)hy?pJuETh?f3Q%<7 z$KJ0qw4*>!?{2^5f4%($`26RqJlOTA_)uis*&w=j(dhctu2Wk!9 zs<4y8Ip)Z+L%P@7#XC}?dGlaPspGdej}*i-Ee;QILFwvJ?r-yrFs$M%*ED3 zD&zzW`$+OX-*mTCoGQ;mXik-eF|CJ?wRfIKZ4W|ATM*^zLu%Iv#8wZO^Vte@T(tPA zLe)XEsQiWvF^T5T+n~^ENEkwsl75xwc6S7}-G8!*Yif$3i9R=zLTL7+<&VgRLV?%$ z=>GW)9PgiA1C$`5q@DY#1TOCn7y)RNws$;XwE%hk_EZ`SqRy$RIx~l$ZiqrV0hlBj z5J_viEsa7KpYXqS?Hue0yp^qrhK6TTa&w>C5*zzZt@wl-e`69KWU2}-+!S058Szh- z2_~8lZkGuv|E!^pi;kDjBDi`!FZ30pn$Pm*7Mi0UgJqwE~H{_<-mJI~F1{$mq@ z62jk-3$%E&hIzA89A_Rt$tA+$%M_SPAKCA`=icV=IxU}|@4e7a5StT7Wsr1W$xwlk zY!dd*z_D-6YW!n6k68Mx8k%fR<#o$XkzIlCqGlwt4I_8_0hF&1a?gE$hQ%*Ywe|_T zi!UH?V8Z$xgq1W>32>wGxdgfLE$1Oj&&DVk5dam58l8xTi#Ei`<1g+%IEfT0LHEKV zC_G1~&vO^W(e+87E5-5Gc#w7o>Q^pP_zCl?mcO92{~QfVUsDN?7My*D^ym##ocoAM z<89=VE_u#AgWPjON#pk*sdwD?@(x!awmEbBGaW?rq~!J7Gl*C+{YX<2lVY0=QMped zXZW%;U9sk+wY!nRXP(@35^+=tF?F4YD6TSVudtywyz)| zS(NnIz!v=) zT`qe_5-M5aEriwEH7Z%OAfN!u`t#<57IC8y!rA=vm;}UUWUc;LRMe&@YFWi{mDc&A zpdfx7Ziz8!y)8bsLGUe|n;|$--;Gobmnkq80*8Ko z-A7a`Zx}UD0*}yu+K?%8FLBQ-E&l`+!HiWQCJYe?s2OJcZcF3ckzQgReK|9gtB}AA zpFi~o%GN$W`OMpBq0rSVeFX0u6~NH66({LhtZyAR<_--|<*O8-a@x&V%ipQ9kOWE1 zQWZIfZXpF27(?3W+YH4NU-n;{Ir?;+JgUygqEp~0>^iEKseBgr^#U#b+{b8G_{#he zMd#i^!Nu26a^*b~ti6Io1H z`X-oh3m7@`bMesMMdVhXvc2mc2E^otyJVqN7@<6OH2_uoRxy87b?IjLz5faW%1=NA zP|;zHGUVj3wpWB>8{-rG8k$Pr8bULrd2S?BQx3lgr$Rl-!}iu9s+w@W?FgJ5CwYt_ zvHg@mLUrzm?HZ=gPa=6>9LF!40RKq^7sr*Ghd@(mZ(9rhzdlIKV#x9q@TJ4=_jtYN=4-uL^ti_ zIkl6H)nK!{`H4!4PKDaA%kd{6R(g3PVZH{hXgaZ)o}{0+74FeHkUo6L5R0RWw1vM{ z{{a;jsliKBBhf)+i}g`Z!qpZE`xY7#g^|^%Sf$X)Be*s^#u}tvLv(O$coeCD3tKFq zEXsUMntb(JN4NGdg+0t1+yTq~o#5wjzjM!r5dnl|X;hFO{d|B2E~)8=^ZX?dQ@$JWH-N)b>Tp;J2v#?mwF3q=Br`b!ErmCKERyMnb$KW!N0sl|qx0O~~ zNnt3iqmm)Yh>6FJ)Ep#Kw(&g=!8u68&`LN-rII^(6P3U{$e6jyl5PrCUNH_VC@fyv zy7&z`7k`dwT0S2kamniIR`Tkef7n8Y@(Yx%5;hYtl+cP6DLLlci#Cl|`SNbk1@#=T z(&GK|mTIdgf%DK36891n^q;b38Br}fpVobdwy42Aa~>8QH{&^Gl z@)Zg>p}lXB%5ITxU*7%2_uyNgu+Myn`spuGGxaH2CcZ`I*e}sB{!6qke2dB@q6I3B zlJoB%kBBGb$T^}63MZ9}I{eB>8{2jOakc!qfRLR6YL${y{Bjrv<&avEx=8Uk+o+6H zd8n>Ohb~*TW5MjBC|-QbA|VL_<4Ei}ZHnMK3rQz4EE=E? zvT8(RfMj^C4WkmEBCvMs3NKRuXezTB*@O_C*uK`DCK51*wxVB}tW2Y3XqO5itUz@Z zEu>hay6&QhfQbCCmr7KXW4?nL+`P#O1qvqV(2WvA3ZMXkPW&6qxC`>+Q!{5`Gqtfi z{Tm`;EXP}g9@bM{-u=ux#8!1!-+ZN{#@6pca?d1EHGi&e!UlJ!Q*HLq8<2D2CTlL4 zLd&mBqG6n>;L|i)DF6+;)R0N_w3nh#ifmltZk`9xNSwp!^tCCJW1#-_vFfKsR1vA3 zfnBsPTC0Mm#@4}6hHx=`6(*SzDKzH z8HMIk_|`r%r6HxDSp>?GE4=i!z=+d6`TLvvP8q>ewz8Hnzs5YyVJ zby*CduR8q{&pvKezi#vs)J+nK&r?V#pha^}6G@yw6seK28e@CN?PVE>3Zgm=TCW1@ z=A+sKq6;g9t{sAN*9$jYF&$@0E*P4&%*G7hr|k3{vXw zQWL5uf?DynjUfe_6t~STv>x(X(=xD0W45S?)d&BY&6OI~M#`1-C$i-iVZ}}7Q<1hT4asFKe;d;r6vg6(&wEhUi7pW}H zzhOD^S%mX~$*HHwZ-;5sj?5zI=s8Ou(nMqR8n7|vLO4&SqcG(yU%g4Vx! z?o-syeT^Cle>0(d>(tNea};*0oBRyTr@un$>2Ik3eud^Sq5=MW@!C5S{8y2G>1E_D zJ%ogvV>ZKJM;V2vn0&sxgVaNNq}*x{MA003st1=_yCJU^R_KIE5;n$WO>l z^9a;sNX1u5yH@={#o9}7?why4D;jT})H6k-ID_P_Gu%JCW;J;k;63XdMr4gYcVMfV z21wbU>T79-5{WXfnFv9z7B)>%wkZ*=9Isha0vDbB}ZF&5v zKvMySpqRY2jua}ATEw*sS}!~CL?y?VgO40PWv5>OXi|)X!AE#mLQ7zys|Q#8lVoY_ zCG6@umwuR=<=D+;Ib3_zkUYTu?;1q{Vf0o~5fznK^`MoQQUMYAX`W%Yb@pvHI%B=y zcutCwyGS>(G#qpG0jmj6IILoc0=W1&+LwQhmL*zCImamQg3fY~>30&C>P157}4iF(v*L`s0d9lLidpaA$8 z6BPb&p2G|UbD2nE@dXO~NgIl)mS7seW#zZ~C+(0jP{`47WC00B7m;{m+4>k{j^B#> zb1$P*5sgb9q5M3N3!#6F@(jk`N89Aj&@=lhR85f<%zT8#i7(MJ@#p9s`!$9CTYmn> z7A;VsRQ~F7@UA_M)MKj%ukW>Cu_|+pAg7N|=c9CuC`&P?oIQi>AyfdtrU)YWH$sa- zl#0q|qH+?cUh2>}zkf$4@6@gE%>5K~S6)XFuTR&R&@*aI-;~}tql%jGD8v*yNJXIj zh~2}MR25?dS(T)sR01hf0BY@}@OQdW(_0~4OE4?& zA3_I01W~0#HyYLcw*>+Gna}s*ZM;EN1*Hix)PgFdmDW~g6ph%nqL)8g^wysFtr6tI_&|ESG}PwSt7qjlJXQP!)N zJh*7~5wrHmI~ORRa{R5pH9*eD*R=hbE~`2AXW`sQtJ*saN9Pz)yN{ao|E_OF*H5Wfs9m6&um%I{Mu6D$e%esay5Gqpvti5f?E>Z%u6!M0}uhB>)P(AlK zYUU}N^AsMU0wwD-Ec}eZ^rdlWWy~pRASauUK+(Ck;aPgx-!e;m^G0vAN;@0Hee4!; z-Lr__O#xQ8QyZEJpltpLG%kOQ`pGZQKnhYjOSC}+P&W4*;`-E@Y(LS?N$bF?e14@M ztDr}TJ_-9~kaFq@($Cz6wDDWuJT8iJpVbyrU;LEU_PRwrDo6vWXFo+VEq~kiw`e)@ z4XUR;Le;bsz~|_h{1v(Xw`e%?1u7Th?|2)93r`~L$OSluCK2A)hi$a<3KNUgZBNa{ z7A5S6gR62=)qE8CYl6dhgisk!0jMUxrYF&e@cFC3Xwu-KpVDcV$Uz>5E+XT^Eyx|c z+xihIi!!$380o^KIfvyZa`sTs^^Cx|mx^xhw9zI}0!RBX#MJIIhp-wfvIv!5 zqNFpvDg@2*6iN-^Uf8alf0}Vg>u+Ih(gE{x1(bjkK%|bGVkG4j|Lsr!J}Q71)v%Wk z4p9O4wIQ{(tiIAvsmZtIc&&Hf#!Rj2c~?ar97hN|LB&``bfw{zoe%gDmDHbRTAu%aFjtvlg4 zbu)?q{@Pf={IcGmYrQ-BUUm0hvoBk5D3seFtL;`0i{O8`Y z!Wk)o{53)5r~O4m)Au8D>ON$V-nhr^GuWHFZ^c4d9TWbld0rE(edpXS&_b?XH$|AwYZEn)=f8FQ8+1+nf=b~-lq|f7g1Kibk0OGc zdwXduwt9;A%qjGlxt7_dUIYFqNV?~te#INCh9124!C{uDqvoSA^?f$2IH8R~y>r?| ztfy1pSpBX3{{>>PzrtjFh&n^mBhmI??J_lvSw#Gs?vXBP9qGvQ?P1 zNUg%S_dPzu<`jJG_W5E1BJ#stQUEFCwTLclL`*sP5O2!%>~g4!t>)S(18pPWl^~U> zw*;#!!9xdcw8mnE=lc+;EU)q&Yr+xXqrj`byg0bpO;?kD*Y4tm*iU#zp4WSZ^kCQ$ zK%^uD`vvngfHQ3mAu~6-y#L1iM*_Y4PVkkALS?L)dG8#Yr6o8(2 z_iG6MD;B977T!V=6+rC-h33p>s9)sQt5gDOp9C@pl}PlVW!ow1PN~FM)Aw1pdzs6 zpk^Q8QMNx~8v7C7F-(g-jx=>2I&!_$RHy+<%0Vgs3cApoi>S}hJ_<(%5B_cH&SfMK z>B%49ARS2RoFp3L-%yD}D?G@1;-K(5sUS7&NRydkHG_bZ&w5&9OO0`207=ryw*nAK^LWG!32n(lY7C?{R@?}5?X(WPqosL|B4_Jn&9dU1Y#=xeXv5m(2b z)eTyBN3q^Say=InK@v5+EWJ4Z$^BD?&YB|7*lO!mqh58g8i%jIwU0b{_XLvm(;DoV zGf#gIA-#q^7y^jk1`PL80ELF+4^ZnaD=`&3)MO(;)(9$xs2H2)r%j&FagZE+kx1iu zc+Nazi8xiu{?J*)@<%8jLdc(a8dXalqiOzUL;#gG*^qu`273Zh~&PoZWqV59} zEWT`oM6S~}AbErv{g^uE+y>9Y19m+&yi_B!4$h%u<{30mSbN9++(K~mL;*q^WecPL z{l~4Rftqz@oVscb#w;QVb70OsX48dTg;S9z9vf8i6Qx1Bh+h ziG-#Bgq1hi*f049RCuNoVk%Ks5e#EGd9! zPd?F@0T0`odHGc?DmR})1lXW(zckrA-8R4FXwEl{CJS*Q_(g)Dg}S79qHMld%` zbkiQh6Jn~1P|3{ms6YQD%1H_2@wawZD<7b4f!6cP#}t_Ns2Ep51X?~WxpN@Z5FMz>&QhU9+Z(X#jrdPe^o9mju#j+4JY#~g)s z`DMgg)Zhe?3CBexa^@aGp_+B@d{onryZjsqR$jKx7HW`Xub=t~jpG#fsc%p_`3Y%- zD9ab{&G9@Jh-_9qLd^x0^}dKQDvhGWm#j26<=~njsvvJAHm0WGS|U3u$Bj+0Jzxb% zI(Q#7P)7CprbKn03H#*$6b+E2l@nGLIHJDCl6+G3P{C8+Gfv#x>kFI=e>c?-lzf%UI-DdI%LVo9T1Jb9ZR*<8Z@~rg#zLbr*~RH z$HqjJVI~{jQ-6D9{%s%vP@1r^?ZQ+5l3QuX8EQx{!zWZ<^ig3 zC~4prw$fUehNnPF0bFC&h!2^YQ3^m0SSilpuvUu9>r=vrJlBG&YUd@#QBY1XH%|kt z%wAfnOYqVW~Nk~oWO2KMg`ozj=OU}KEGG*DB5R$(y`J)F4zbOo- zgrgz~xfLN@c*~k>x+m{J(uwng^7l|tyoBm`zURni=sfvLbe{eNTB!gkmtI1GX6@}d zjih1zEfs(ue&t14aSE_#hj;Bw3)huXsH<1LMBV(?JhyMqboy&FkA8zzIrdI}fvR~$ z72ZX~oWH`a?7}<#7GRV25s}=$`*;#z)!jrx1t$2{s7HfJwBIirWgy z2$^XpG~kp*Vw2F43=bkA`F4~LB*$P< z&oq)ci0(QA2tdx@ekuYAe=@DVXh3`m_f!3#IhRE#b;!vaB}cP-PL>nVW7USyb^W9S zjo9w?nj=tXMpQt;e=VuuhUgelLOHHQ5na4SB7sbW7ClHWE<;Oq-w)dfSk=}xnNjGS zjLY(;S#GBAZ-{Z2RyS=)Ep8>95PK$}cA*~3wEBkCE>xWT#NN|t2v$C%viR8Y z_XYJ^#=k|=$d_mt`5Ag9evQ`CUlFZH5xq$|!}C~FA=L9kAym5i=Lo@1Q~0}}w%8k! zT(~wa&JuaH68&uz4b*ejK#}Segw}N;w6+bQKFv)mBm&I9R^G#FsC>=xOZaJRHLP?; zP8kx((=~l5=hR)KFp7rUhD?QD$L~SrsXKX)){qXQAHC5cJ`(<z#Ey|b*ILUt73fk<}N(SC~+} z1J#u`+8~yH$U~SZfyRIrwEVL^Ph5|pndkVopQ7%}7igdQ1)3(lBv=0!-i7C^vd|_Qjo)d# z`m9?IQH4r|%ScD8m}j26f9iddoqh#vwD_%~KS%S)pP_5~R~VT4@6k5>CCcVrw?Qby zL?QWePa%8kDq_^?yp8CL6l!Z;2{veu3Gaon=>+e$s5ne9c3S=&6%BlL2Q3jNu5&*U zT6ZG6xDr8Wnb?w`5?$^WT2$?sptNj6iXvuuspv=e+^$FA=)HWV*YVks%J6ySj6aCX z5ek36!KKaGVQU6bVfhyBKc@ZYZhgaQKJTgX{aCknrO zQ8wJPkNdS3Q595LN&rePq_Io!(?BYGQI%_AdGMJF1qdCQqm!t2D=F`eM8~gs2lxDN zQi8L*-t6+`#z&&kQfc{U;l+XEAd`om+D{H2Ljb%n)}@D=Ae@#nQhfu3wh_aL z0>Y^XsL0fcS`%SHs#*<^D5qZi@x$x;5Gt>+V#uUGTU~^^REkm}*K$*9X52P+5#cVc zpF$Gdyc1zf{l>`)PTx!3{slT_e}P(M?h(RQ&wq~kxvz)@tNx@R%faXO zauOC32^5}viCp*&i-VU=KWmLPYNkFRXa5w{lOLgS{zK$W{}c)PspyVgu)ckTO6@&Q zMRJZ1o^->vOl3o8Uwh_rG);bm=CLo(dFq$6_&-Mz;eXrY&(SjT6{=_6MD|GvpxS`Z z+9vH=q-7qV;PfK6xW-_=qC0y27BeT>GoDYSslhOs3asa+)Ma%Uif-Fac->1BRYc|L zB$bNsD*y`7wY>j3GV>6vS^HD~$)suN2QR@*N~aWMNB@jbysQ(X0!MEq{kYj`7Nq3; zC~v_J|93B%5^(kNdny5;MpcN$wjD7Cp!z77@JsL;hs-gkYSKv3P4fj*aDSGnB^%GZ zUOoc#l;}iQe!aC;Q=2ha{qGE&kdGx1k?L-8NC~843lZXGXA>b=c<%`tixt;(oEGQ+V%o?P`=(6^q{;y( z*spM)p#K3mj&6fz>S1`s?lmQ$s=##qK6$@C^DTa8l7GL7wEi)KdaJO34^24$mJ6V2 zFh1xlgt5UTb>@-Q(9JkC9#JMYC3xpwMm4R!!f;9`5+YEtk!tJn zR-dy8JW7VCn0dh*i}kepP2*qlAO)aR&zX1-@dv2@j$gLkeabZ`k*`7h`j$UJ9fiAT z>}#}~{uUh+{GPF2qx;maY5kQ?An*T|g#K@%VCr#;prr0UPXU}Xil&_RV4GP$8l>hO z6x{E|ma8*#a7jJFs30U<8v3DBTvaaWc}6zuf<}sq8mKvk;J-TVNRX6`7ta3=&-Gb5 zbRhsyI>rArnOXA(<=4qnR_DQs<^yo^xw=o@<4+M53fxI5&`-4wYUxHsTiE)MX(ef zkgjWlwuNl}bfPCi~NAEJ1_=J2Z}l646x@;9(F!LO5m)5;TKiEAmvQ+?~A8mW0{ z`E91WLVkq(ntQLL4I8zszcVW`Y=@(NoCx4JB3t)Zv>>r_6iK`0{YgFw|55n;++PmC z8!eMgia?4Xb@!?z0wwL9r2wk|#t8p+4rzOr2~Q7WlPAwCxKRMo0d3IDq%z|EajIO~ zJq8D9gY(n{I8L5J!jXCZdDit?s<8SMY^T=O|6ChMIzVW+EuCD~or~>!kFcsvWF1~b z)5;s@TKNVw)1O-0T^W2$)8C+C;tk|aJ%#+4$5BTlP&fAlYGL0}9T)ik#JF z%n_!XhVq#gDex5LNmb(g3Z0|BLdVhHKqE7RJ0gs(|uC1l16_kb1{YrTs;p}io88X|L>0iDEGp@m&pCo^ATNC zjd0RM`CO8^PuPUvq(A|e6o6_yGDz>7dlr#;gGs+d$~W`3=#Rxhc5GXB(? zfQBusu);b@iTf+R#qu2zU8Dx2zZ8Bskgo|>$dG#|GVD8v3XeuqX#0p4K6{^N_y>K+gn{ zhsgUB_LBloP5_UzeN+NRuD39n_+lF2q&ARiU=9f#C#=Rmwf1qO6wbjJWE@yA3!t{s z>Nae}K_Ow-7#&YUlZ%kbWM5_Nl`8NOa!A9 zE({^$4^d~JoDxeJ-;(d;^Vwz3If1m#*-swN=Vtz%eP^xkC;7mtKg)CYW}^l|fZt_+ zDgbQe12!d?_eQ<}84)=`HxmV38%D$&t`Ku5B3wkuVPQ=P zDH(suSSmvYLjc*EJHjz7o9^QLW6 zM0X#yII&}=oO* z*{$0BJ)}1cyf<$1lPKR^&LZnflc-2RA-QHJlBjsx2W~+1!t1D;{gjaWOLR{D0Gwd|)sAWb1%(eG_8WEqu!?Nc87 zTHT~<#v|7S{vYYJuC4ul)118x@@UJ!K?^T_qnN&uACwWLjV%xVcEUjw z@2GN1ZKl+xPlXazH6P9@iDc7h;JJp0tw>l^Q&@{Ron&;d4f=qGa|7=DPHBMV z-4;5Mvq)SvfzUF%R*I;{`tR!;)Id~y^elwbF@b1md}(xL`^l*(pNDwe4#K_&@^}^0 zxF&D1$w0+d-bLwk@1yYY>qwct-Dp7sm4IU2LIS#1*1=ecM3s-^zIre9PZaVg9=i{< zD<7b0>^-!N{~RrZ=i>4$v)@oL_~Y<$2&&$oRmG@D;_A$Jd z-i7xvX~Ocea2#1i>d5tQFFp$Ig*Q-s;X{<2CHG$-QkeJ>9fJP`{r?r$n^P9+6xgQC`i{#zAur*Ipe{--gHqnG%hGc6}hUJ1pl(g6wG|iY$`!BVB)0nOB zYVHrx!FUR%th*F=+VJ&A<8{aGI&Cz=xo3e?fbgHs-TXd0B=~X!iu)(`%~}$W#=h(Q zLd*QZEf2pX0SR$Z0mQWtUA7!BACwB%WcAHC&96g>8WCPXg&@aZ36(%`tL;neZ7X$G z2s@wqo(N`JaxS625U~c8NM7QJAV~>C3;6YR8B)^2jYLq^jD-85q@h8{LneFQ`x+V= z+Sbs~aVR!5{g5li^A{1x$%nQ?MgKb`#n?LS<%i5X(kVA#aKo3B^fSLhn<3Y3jQ8l}fzy}dat*-}fd`uL9 ze-oqFJuP?API7%}^`!n8q5(d%#=S%aqsSS)3B}WoA?pN%{Ky3xj;RT~8s}}(bI0!X zBLKq3gkGWt!a%jbwhh4tDkY%Vea=C`OwtJ_Ib&F53zb;0aa@}_7ok``l>n_%e8+w` z56&ZN?tT(iZMS-2O2H*S6cxROJjPTqvihdd$nOAZ4&Ip6``g zf$SsaP_pzQ%BSB#(;33}(?2Ia{{?yp%bO;@v_1lrN*7*!%Q_Jk(VAzB+-gq1yvc{* zU3kS3iPWh`Nk>w4iTh`eP5!O{o!$#?q2iL-c7Kfeg)a&9zd`%(&k6m1iOy5}j8k8u zcI;zR&3%OYGmj(v=s8|{JHm+KLb40Yk!hZ7S$uxKHMhhZeag;MZvn^18qyc^nBk~$*~3UgQ3;d&aCDLREfh4q|f(FSQz;9zvXk{!y@vNKXo4xca0LY zH6bY7gROG#Ig;)Cx8;{$TZs?bE9xmc0~DUah$p|#Sh^R5x4eNuUVG8?Z^C=sD@Y>u zPaK+{K<%=@nc6oN_De{zHs4XcHdDZ+==sd$>(OLsRf;OVB7%Y@NN#1oRQl=U`Nd1m zqhRq_q@B9eQlTaMN)fg~uMsMGO=04FQ8?K00{DOa_F%`kueg^a(272Be@X$Umt=hZ zX{!>nSq!}=Ec|S_0yGGssj~9c@rO#oPYqiA4s5=U4BmIuuR;P+0Kx7`gp$QQK-iCpyY**vJ@+siz^%v-fY9+w3_I!}00C>}+ z5F*Q{00`x!1fo>cXTqRUw0z_KL)NnsepsOYw7K&g8dQ80Nox$&bYJoAWIL*>t##iYO}M3k~?4vD=d&G(?j zprIPat@-yBX*iDLp#`MVnkep_F>(Xq$zherrhJEM;tAmc`42mC%Me!AgXH6@aIZXs z+>6hl@cK94x$**1=C2~|z_isKgjIJUxU`nsUumVZ3Q^>QN+G5akRvs$sKSzls^*_Z z;{pYA@(UBn*3n<0cAC(C`CXf~tEsyhsG(#Sg~#OROd485_UXG&e*S&rt-gS4^%6LF z0};TiU9S<4-NV8s~pz?YnwTi~Il5iix@}eGTu_12z{Rv|5f=uOUC> zc4+qAHcfn{@P{@~aJu$WaE|#c@W=(EFW*n(^9Z~jUFF%i*z^>iP{>)IK0PPo52qtunAhuzjA&!s|?o%oQwUkq*p!nV6 zmKH9Du=$@XMY!MoRCkvKA%X}Z0JUdV=PQ#3b-kid)U3y?ZZD5~1ZCx# z)xVa8R4vLy{kF&$C~_b=#jh=dD4I4h3?|0;tA>3Qbi>Bt2#^Xg>C)9w347ZZrj=&@v za({IRl1EjXi#JUbf6}Tp?#eO(MFr#>)Ynu7@(I)pSPwpVf7JyhzUR0d>gafnkSS$$oFsa4{A`hVO1B>2G1b>_zlP#xdU0U3~~S-zuBxpVy`O8mXLN} zjn@4%Vj9Q|#r3MYt^c23pmGEh{>oc=0@+KCB4he4Zt9DOY8fQbsh|cYuU9<)ALizq z0)({=Aok!HB%QvD?1cxAv2YKZ6IaX$XI*^yPVl{V6AiRk;*6l5KkG2Ux*u=RkkkBP zUehGXsI+S4-$vuqS7@93)+z^!7hbg{pROb74s_8v2D=WNyoHz^T_!T(EE+g*XBqF-@B7_Pg zq@;$HTvLch6$$@?vNS)Uj0%1L2}A}NBedMZ*CB1+A|e|4tx+bHOAQf!t2knoe!%c`mVU7;CsTJezeSLjEMARG8mzzY@cm+%|>;VXH+ z93;l|dBbFpr8zCzuUt2-<+tA(=dPp@kkHF9CeYE9il*hecILrdV;N62~d7UV2E0ngdztb4C#=`m7@>uD*D8?4_#!QZH! zhUrv{r4)|FPDJ+-t?ZdXyr$?LS%8BuRg-n)u!`wEYGtRIyt_3c&%$Wm^}!RhP!`P`UOInrD87{`p^_e)dD;oVW`KR8rf^s*rJ!hE5F)2=)^VhziKyN!UpNjQR$K-k9)95vWrTf476iavPQ2=CmBU zpF)3TY<0+|#zV3v<7&ACNkjqM7uQC`AUZYupLt3u{=!>RcY8o||J#?hy1FAwW3>4p zg8hn3|5o^UavS2D{*aYC-f<3_wZ1()*Z7RuIhg{`5JZor?2`BBCW!xt`%^>uExS6* z3i9)7!t3a&9)$4WhnK5gAcefNnK!wMH(tWu<8RGPB@rfwML1>Mf2sWyvsIJIctXXv z_Jc@lKMq&d86>xj!KI2X<$Mp!BBuEu;dO&eCQ@y-EUb!qf(0XKSp@Tv`X-I{C-=>e zI?N;E2(8b^Eyy@_15tr;#wX1u5J$)vN3;>wd&)ZRxJeyy7N4-*`0C>8Irl6)i;uuF zb~BO&PGCn#EjDN8VsmajHWw6OYY{)Ul8~^G_n=MIg&VnTKY8ddH}pYn+OO+1wGU9VOzTfrFMo#m04OQOv3rIPeAEuLLwD?& zM0DRUvX7ld!hm|>4_m_%&EC%(xrwyls#Vch4}kffB6s;|lr6rAs)g54x$qn^i7KKR zcVb&Eg;xea4lL!hr|?yr+@?R3QD|DtIx9UkqP`Eog!sW_4cJ~n)KFMPg_w?O5*_|T zDpi8=dcyc@G{Q@noUY;X$eFnxc`Hw%Xz3ve=>mEFUhF6%edNAKAq7kAJx0Oi-|kvK z&hV`$9J?14OHZTf?5k8%cOa(efMrx_nvt_>1Q`@;_p$5v92b$$F=GFl%C9p>_p;PD zlnOBO#LfOp!QBhCkK~Ke*fj0INbc8Y8YAWWYo?za`Z*8(-}E6;RFza#&OcKCs$cO^ zz*YZW;BOQvs~<|)2rnW+)|hb8WQ|?h>Mr6vB?Y34ZR2Ywk)9OfcM8DJ0DmV1Am`v# zB@%HzSal?6xWb5W++#Uq6~Bo{O-&4l^51L`R=l}^ymMnrA{Br_I()!NW8}lOjIOvu zi|wl-%aVJjsT8`|lHs9X<(Y74K*x4B;hN&Mngi&e^(&wfC?@YGeA`hv`0x|DjuSOpA$_+HMN= z8I+%W7NzH3K|Xb7^pOAnSb|AJK~$l8%8`ry8G^m1E%YhKYVE#yXpK9L`om@Xc?^GU zeF?fwAfcZYn#w_xM)mVnK}SW9J^KhMc^z5DuSe4EF$#2_ou?)&8zM9`7=Wp|r$Pal zxkOmJHbQxETRH613`GS$8;OE85Di`HOfnQ{dxXY9NKcU!_sN@R!5>5M%5x}OdJfrV z9)xq>oF)21)ihyif!FAp=v5f+qehy?)a^w=>#&7|lXi{~&GFh>4%qV!$}hLP1SzE0 zX8s+YWfZM+6s>wp^APU`pKIG8Kb7R62C{KYhmg>?A2G7-6{M;aEfi)dE_Wdh8Vx=> z?M)3Mks{odSBxE0*6JB3ftPXe(^?w4@_^!_;!?O$6|Gw{{Iao)&sXoG;jJ2TuDKTG z09FSn3jQ_>9(r${|^g_LB5n`|GUE;j}Ftc1u?Wr?J$ELthksYF?X z`kI1ttt!a^!Y@UzIfhhABe8fbTMJ6e!loRUC4?8}Uo$~APcIMtQk>D`w^8+eC82a@$Iy&51$7=GFFryVL8YMoQ(nF)r5p)=2JfFA z`dfax?y2~x^>F0(3CXYln+d$8q-6e{o0Cc-h(Z&}`x`-_ilNm^Y&nFqp;_dO+=h~= zC*eEKdv@U+6fBd<9KXqujO29PQNU+Gg(<~xtvUx;yqxzzmN1nHCzH>Y&u%LXhI|B? z;h?c{n=`p@)K7p4NKHG`kw-@m_sl2%Sk7l$z&)hTQB^4aM#F1uekJdf{lC7~ zKGz}PPL)ih;2I)uO)r+b$q-1M40S0YK%=`sA$JSA>s%<0q7AAq(0x~>ZD{zvib_fQ zO<`5-WI)va)8+H|HpIEzk8M_&VuCiCa<0{IF@y?0Nf=S)eO5@q+IHzdf2Z;FDg_|H zH+aJ1I|ZN%hydkmzS=4BnxGPVuxL=0;juy@;>$u4+>4nNg|Es^#MN9FhJB%(q za1#U?wwCj?F z<$KCn$!jm|Y?ezi=Y(qb|9Th(TXpE3@qBG|U}cAK-jw~TaG$!vCfk;rf7j;yc{S_q zA`!v4S1j=+Z~6gR*-MBa zQo&1f&oE-h+wCCVSM{6DGe~pwx%sxzLRq?ZtcGK_EPYwywZ98h87iQH+QvPygU>0L zN;I^rk>{ci>@Zy2TKyHWRKi#gr@i8)^bbs7=ji@Y^(|th)bIa7NQs6#JA0GeOtS^?vp_ zdT+i5Z?1Bw#otvSW|V{vcMC1^)&go*AqUf@{26b=7Truea0>8N4?jDb+E+JEpT(QH zRS%gr&ICz{L+>9rwgwKhZPJF>DnS!u*Td8M@(|ooqYATZgn`iweT0=NI2u9b@J+~_ ze+u4w`Ppp%#zr6NcRII&?(v_EycKEWtYOZ0Bh@%lfJkO03CKXDV5>Sy5H-ScA zN${@;_49va2rA1`)rcFZM7I$IgiyeY4k=r)ewS6PS=lPzJCtZf*B(Sm6jIVafvPd3 zBd9I~C)yQUR7ZyO$dr4&FN|Kc0tmMpmMdmFULd zT2(gEGHEk!;r&i(8$!;ZCF9*{bW*(bG-@uqjr!&H(Z2jSx)#6S@g>^Ver9FLzJ>Qt zI{gfCj^02-bqo`~aJN`xo^e1 zL4!sYN*9a1b}aX0`w3b%%N@Ed5u*LE_tv zAenI7J##+_R$jE`on_1Kp#0|{syhC+tRt^y>`u#7&poz=4-pN;p8oMQNWyCN=8w?0e^a|QmmDMrczqfEht2$RBHJ+f{PpcoR$i~ zHXApr9HLf>Av8cG5X#@naTldR7%CETzViNV@#G_+c>o!`hY{|rw(QPrDWqQ+McAHM zWIgcJ@FQjaX=I;VB88YBB{_>MUYj}!Ylw+*50ql;oBarllV74)y#=PeLe1>QD4l&3 zxhL;|Yu6dXQ;CM~Ty&0pBvwrHNF=6#pnOhxE}P<-cr9*HDs3{%~L3?Btp{C;~j_p1a{Ld<=-gXnHMKVM_!w~}581?}KI z6rB)_P}{m4+=D893gIE&M6eWUG3iAiX(|=3l9?prb`4ZKW@L0NGG5#_`u%pEheD`k zkcdt=&DU}9^`!kfNW(Tifn~b3H zG?bhc8j3&5D=oiTSXq75TibXwE<5jPNNVQQ&`kH$t%=E3lM74i=ib_;t0g=Ns_L(X zr(|ADFR8rhZEU$(P+fo3Q&oL6v%K*Uvod4*TE#5w&(Lh{v6o^LjQ32O(um)~F=u;Ibv4C>OtM znFwI@WBAT~g8BJyczblyKJ#P?87PXPiOI16i>_oN z^Pgzr7Sj5wY*lois@syD{59^q<}6F!H-VCgyD7X6o6|9M*C<>AYN0(rfjxx8)}u&0 zvIft}Bk*2&2AR_uTXr4NMtOdxZ?c{Q>NHfg_AY8xK1JQaXQ-N^LY#g9xkqk6s;Vw| zuEA9NLGFS#xRI|$y7R7vy9=&vNp|x{ze;p?brYXY5cjEv`=z9*%?>a3oeu56JnkP) zIrmaI_hq$zPimj-&>rOf>d^ay^Sxv8yjNqmUlaLkQ_8FO+%m5^i%N)eX~Zb7LQAo= z{fMvGX&(Ow-qXaUA;c);ryLdKt(3GO+=kn<`ICOAh@*Y&Q2B34Mb*F6d+y*7#C^ZT znaR)Qb;NNWC8TnnS8?yOT}>z`xw@6tCmBicmU7S4QzqOzXQv$@DJfU8t9iXGZM>ew z-^yT2W;SJudp6O@_w*kdVw{xYRLV5viu*d+Q*gDQvGHm-_o?=KIro2n0Q~W;`rchH z#TM5hhWyXsuWA6IL)C$_Zq^!1bN|&lpu8LLjR)cCJPl|2DALv6y>*BWFxQ4U3bt)? zIEs9oo&RlNk^jo&^j5mE%4iiRWRbO9i0c|g#^Fn-Sb7%K=iegNB@{mU0qQS)fbw&% zqG0)PY-&+oqq+@tAyzDFT*|c04mPE2Ji9Xd?}%TYLriwD`%p=PgBBot}44=%TVbAH}=f zJN$R?`(MS4jdjuP9E4CY#u5Rgc8?&Xo6n*BgiV-ErNWDASHsXDL{;syh9Hq;-Tv3g zJ}QPD8lX<3l-HxRzdt|lw(IM^b~O&{`HL{}S_!??0R%Dul^v+|QEHzRElWj>tBF9- z^?MQ9a)=Om+=_%ExWVOI+!Pb@2W!3DHaxs3r=s$oiv9!<5=K!mIS0q#7&=49PoX$Y z%Q$kIB@HDUoOSJ@osmi`mPPVE59Yg|2U1M;z9IyrqDxC1Ox1SBX z?f1U6#3qbvBj*bd2UqLp(pn2Wi5?{M9D{5BJkpO*um?x2by&*&6=c%lD!Vb`z$#pX z_emoc;TSm&$M7l=_RqqxXNG@At9$qgGEdx&g6YRmbLBncEj(-qG%0(g5up-hLDS+k zE5%jp-om7cYREyWsE9dgv&c<7#)TC|Q$mW+RSXeG{^)f?EKj20!pEpO_dXgfeMHpq z2`ZQ0hIi&!>m^V|`jUC#21NB7L`*Mfz_E)~Q;>P;b~uSh)QU|gLVgI%gH+Y0ae7li zg~qexiwMumhPS?cEpXfZU~7AN_RZl+9HJ3P?Iqf3Ifl6AqgExTITNZqQ5}f&rI2A# zzKN{AirDylY!y*yZt>qsEiFwAyyg1(o2;g$rfr!OwcMDroB<*Cr(yP5++Eg8X~k;C zB#NLG&e4tgsI5;SxoeWg7~%=hw##vo==j%xxBcE%vL|n3drBrbVI{WbS5N>8EOH@7 zkb3_`w+zCubId<_o5B*yji*dQ<(((=oKCoz6gE{J)xbodCG^hLEOhb?(8Sc{Z1>Uaz&VSebFe@ZI=aIcI&s6Ufz)y2s&cKZ%sq!-y$v zun=Z?aq%Cl_j+h%_E+-gTUMGX=PV4GYhGXFfLs4V`8bp|%dh#c214<9Ylqgj zo11U1HKVZ5p|2C6%6_cn?@2E*M$VyP?K#xaDyQt3Gf#em{zsLI>T09+r8TxPYu-2A zN4-W8Ere9_eHwca-82Yi?`ilJpFzd)E2vm_7KJBoMD;@#C0C=qkt;j3mTdz?alHQ zB;*(W?V9%U`(7Qp_O~XL)FMWyx8?_sBe2ti-wKOt1FLs{6u_WaMC%byKZxj>ok&+F zpvqPRXSn~u>G8S(Z~MKjg4%|!wBeNVY*l z2B(Q8$ZNZgk;76UH0(xv*Kuu+=r&6w@`oX4P+j>L?NIOt(6n8m-mz? zOYP3pB$HfN*@lVxW{^30n@#;m+OuR9Ukb;#GS5>2B&DsY&mcm7C9haGP6;qP6xz%x zDgA>hfUJfdWD;>@?wUnX-!zq=#=@VnG-^lp2$jG|ga6iZqG_L{4@dF7+l&C-`xq)> zXGsk@2KWByH1qsZS-}~ej8Ia6$a3`s+h%otSY@M<152M-ks-9#K|t85~w7M>>zC!ChQ-vL>yUL zbL8dM5k8Yw3LP}?8r2br0;cd_bT@^+ZNG*0LKPM9HXwlrBICd`x^8_Nl`GHFia(3; zwHHyk@C*v4A4aa?@tP!j;zoEVxCPS>p=9Mna{8xHxAZ1jRzF4K+9${zy$1=qP9w5? zuV3*e3bq%6I zloHsQoF2yC3V+ zN(4!>v{O03f(xtRk`wI!p>zLfgi~RKQfSmiK}s%8ZO;2Nul@*80I!$VsvLRc`3prz znS{7WGnAZEUX9W-x1;&G_fe-@gYz2C{T_S^@1kn?LzFGOjpF&&s37<=e(#(A08R5> zpndKev@Cs!s`(d?*gHzW??zZ%C-1jHv9xqV7~%Fc?|b_(xYT6h=q2kayQ;2#VDDmKrOHe{5kZFG)q=d)?8Xi#UBhZdzzZU(#xCnBv$wT+m!w@ zNZoT5sk>H@+%aXn0X11FxqE^LYsl!BIhzUjzw`WCKL8@q*oscX7nJ=h@V4vg_p!38 zn=ZzcwIIr;n@--v2OzEN+BJ&Afs_8Q46UPj0>t;9w1|KnnD|+I<547cjv#@MH!j!v zSBcpLdjoI#y{)MH{7qXDT~BC0hNX)tO_RdEL79EYspg@NZrp{eVL93^S}Lxx^1_Om zj6cUwsMIh-k%HLXyl!Ow*zJ=8(LLNMM)0Ifxp8s;P)^enXpn zAB`C!DQ6m%^0w}E1@QY^6|FtjA)5s2=pmdVcTMj;gzUyHc%vq_u6gu($#NN|z?Ooz#y@-rs*CA=|JR<6d77EH} zVM#BNGOPkm6Lr(NPN3lUJ!rk~wG9+$vd%%jK6K47DZP(3*XJ3L-&Ei6SCX-Pt-N^Ykq{AVffD{xZq6AsQI<=6!z9bQAi z-We18}&L|{1O$@Pa$Dwiq=lO6B|e^N)VK<4nO5^QgLP; zT|zdMll4m^{FehzEzdR0IQ`VkNIrVb6tbLu34Ny#+jY#4M6kOITN0F~P6eRak7Npe zTmoV;vd;%@%lEe2<+V;1ugyVZlDU5s>H9ArMWe$`-Da)OY@9fipnK#FWRKitt>kP@ z;CI4b?RU^dl5&Rg{zm4MAR)gbE%5g1>-V|(h7P5MW)~fbE~!JDChrmnsNB^WG&T^T z5t7L%me@BMnE0dVzt`Ikkn|%r!nteCIt8Z_)+IIWQ%LI3z*~Q>%Vr`bs~JX)RgXs}5PkxfF+NyPP@L=>+@VY_JlEUBBcrsJSF*wh9qao4yZ0QcBkD4Kl~p3z$g zS;rAx+k;?l9YTt0ts{?PVA9HME6Md6rZrLME3}VO@JGHu+eyAo`~nSAU!rC0OB9UX zW4Q)#?FSHA-iYAxI)u0MBmKZMyc2gIckC*a({&xUn9PN4#bj`^RmyfqPlTk^fF$QKt3wkk@(>VtbAwwq4n3Lx}G_0mslRa>nmR z&Dy7EUHTb%=YECWGk=b*Q@=*n=&#W>^$S$be1eMAw}~chAb!`{$8!>JB(}&)Muf*8zSu?{D_h!zfz%DbkN$v36s>O_Ajht}M<1pY^Vje?ksQ zp$bupl!BuFF>pJ+mzD2p3(c)<{Z&j^8&Z2GtpR9?WR#jfuaEydyUw zd;d8&x{h1!iAK?f7PlbO+wAW}SxF>T(ula6;(ru)`}OsGugub#{}5l@PK()R0;2|_ z!L)j6x}fn>8VfFntj%9E^8^ZJ9`|>D?IjB6UqFf}fhi=_^kP$D>eaZ6jQxSP{=Ki5 z%*?8glr&gWK+Zt<0;B*kbFKVUDb6vCLp;wZI5e1q0w33N!fFJZ`)18QphBVKeT1+l zZ$tLXBgkKT7KKaCQVHCK_@QxH@?p3r{H{a#o9j`z`Uc8G17<&>#s9+k{kNR?IocM! zLDj;$$flA^KDcbdIpe4VqT2T&O=-%fZ-M8`1IQ#qmXlAd#&jQrs`95Fhj*EZVftP; z4iYL8{#*F9lFy{P2O*vc1gClYt>Y5a!HCzWrYea^$Sf}X-V}h(xBBCpn%4K@eBDUj z&3m$IiQJyTuO^^}FT;I;+<*9HQwEwOmC!X}183xGvN84Q^QQ?wB^^9k5mnj>N0AQ| zE$#nlla2ND{jB1KzTn)37DTJdjgLs6tkcGfDZZ{2WSSk|I+-d>R>kGyVw*DIzT?Ge4w02>y%4wO zQwjJwkwMCmJTPOhI-+qG>B}^d4=f{zf~m%t@v_830U4urz&-U43YT9%9~z6-)Z{Fkbz3OKwKNG|cU6;?N920r5V zvTD1!LbfNRysMC)HK<6F^;dFGt`+L&HOyNNN9$oCfN^U&s!`$bYJa_d)dpP1p&8eA z(%O8d3|~RoDO&28$A|=;gGbeP<9Ab0EFp1l%G!@>9zgcg-6&Xk0!8!Bpm^qa@_N3W zd6M_&0aKRdxa&QE*nv|f?3$9SVJYb+DImjy$7&m{>B$p!B7gZgtMAw3;pD?>{?H^* zi|REL8<&G@OJch9rrY6GTew_o%~gYwLTpRTMtoN8UpDsj{ay=ZvPxmGml4uRADep#jVFMEL z%l}ZxWb5nyY!x=QzY|_iZLON4loRi3wb=sd8z67A_sqS>pZO`$2xT=ID|MeH!)g@N zq-D5;Rdqp=g(La3rV2+ER3Wjb?C)n+)H?zn@jGAnzS^qjEDF4}H*;IRJ6V3!3s_g> zbPqy#O`+v2h@+5mH=( z$iffsASFXNEp$0XOgEs8u zraX#bZd@-BKOnD}>spS|=r3eLR(@5Ogebm4X6t-XZo*+-Fa@;0QYe*qPUWhhql5WO^83pZ8! zsb5`8sn0qpZ!aLVDQ%>Zt;JSH8nR1&PYS^2`|raV}2*g+t^!`Sl+}9MyOoTWYvN416U(Ul?syv1mzY} z3HV2*$G0AVo9C2zKr+TXrh3KmfZ-g>7XKL!j6&}YtErgL7`QIxGh7LV3f3P9$6}6qU9)+ zJ_WsV6bW5a0$sZYe@iGdcnvv%db=SA?hRA3JLLk;aGW z>YB2!PWqu6%mSnzyv`DW$ zKw3q2RNy22o0kwkVp*g4Ey8^5mdmg1JnB_%J>uiH+ZHwD1!&m@A%JWYx*eh;!IA27O+cJ#6#3oE@= z;*Zi|E7}p^tw%&bwfQj=_LK83sGtOCR1~?#uA-0#ERxhFC^ef3pze2~0Mg5=yosd^ z)~r*g&ogp2ybCYzcp15qq$zynNknz=%}4x2W28Ik^5g6nM^fh`oHP`1ZM+WUT(lgr zMj-LL=i$YTh|eqf7oPI+u)s&HuRrz`nwA$8onQV{Qp0|v4=(!W`Ss1(kkAwgxk6Yr z0F?Y#jaw)IG~cZQcJrRQK-wowTg-)=~kK z)I;f{YQQ1Cg=QO=^{3EixQXKBst?$nU1Ho^U4WFP>o~M*DYx>-yC?5Q-qO=V0dJt> z?3<(lFTpeW7zOoS9(Tcc>?{(FEWkOug7lFakVoMyU3>MwDbbZktXsZ+NaTGG9-{P@FHh$g5+hp&Tka2nA{zozBa6kW~86Pi$q zYmKwKP1T7geIA-@X9)I>#d{S&%JZ8EU@0j`00#8c?44Z6+v8$cAPOt8D?pE--m z%K{&@zW!8JbYWpgM1Ix3%px~+?V{CdA-`)n0_VV#`2Y+BNa%?G6d6z~*nNVQ^4KjX zJVQ=>=2;5jNM4H(L3%tyKvW7R{)O?>Gbp z;ko#^bqkULARX9B=&XM3+cFBQrMKW|T>oh}_s$~a;8_afMN)xFNI7u@?$cM{8M_ym zBZRs~FIq?|;W(IVK-O{_^uESbGtj z-wk}!`ubB_ExU%^j4f^WdyaZ?+s1>4ZQPI8_M=1rtE2>%DMaM{2Uf|eDL|+0rqzGc z(uOtCt6=6?cxd?xCZ6&;kcO!MRDHK+9%(zt7wdblBfC)W@GnbRdJ+O3`Cq(pe2rDH z=6LJl#ygJW!Ex&}qrs<|muU0fQZl>g=u0fe?ss=EFo=lSz< z$U3%4K|f*LePYD#)d<8|kr5*6JoxiOT3cr?sRKE+%7kNTx_O>;eg$TeoM`<^>dZM< za^`k;NH3iIr=XGXL9RS(ij4moB?39?`h#+sy5CHz>qIi)dhTH=Hl=)z+-}{KlaHN8 z!7S0n(yPdudxrGqDqK4k$>-HKVm}-d{?vW`G2v+kiE0j9w#Y#8z$}v5h7nWNf`p=~ zf8s8$`YiB~>+4T-Mdg>pCwS|>bu{fm3N4nPU&`(oWF0<>^n&iF>(8XjKOZ6P0{Jp-ZBmrSzUef_moHH2+HFrGbz9FIqWe zwN+8-fnVBT4k^W-6KP!|YJ06akj6bJMfjS8L~KoQ+b|S4*uqLR{ixn1(8kb`ss*~U zf7ChzMK%+P^H5HM5QG2FkdIy@Q1PV^!K6~@3BqR_xq+LW2!Vw zITYql@%cJh_Kt(rdQ5o;5_V@7Ek3V1szKF>oz`S@M{y0JtGf_d-hfS+ehQ;VMobG4 zlllTuN$DI_+PuSEVP(Ownc06M1&~-;`+7$27`!L%MCtU?D4u@_B}*?N=gdP$8@ttL zkHVK(qjw>5>~6mT7+kh4KPfwxi1uzW^pvvaEce2iy=LrPg|qD#5{M8;ACX;IeM{iu z*4Ll<%FoaLNmyPblH0kFl}ytxWSd=8gOMGy2z%!&yDfXlA37^sd=dGYgRj=sv@GeQ z3WAFAA}4dBq#nIYYjYcls2~bY@%8W$vRnK9P2l7Hi&jpVuPTZH5Ngll;Z%cB34N?Wb(P(SZJ^N8f@e{P z#q$;7Q-VxbSu29_%593W#*JzAU=%+qxTKcno{4LdT(tDnaP6HY>Nsx*Lgx{ZTZ_=_ z3T#PCLvnKRe^rh}W=Zu~S4sW9&L+aicc+NP%-z0U8e zIB|$L~ZY|4ua`sfVu^HAov;MH=aiqr3^OvdZoRpKpWD=lgNsW7pRY zVddu!|HM&T|7|2KsZxex>$tIH`Ff7|$9A2%#p2!R^8U|0gQEFoEeGCtWEIJT#mW*U%F+>2GQSYBtoM&mz39Sc_pyr%ydu=>= z7==-D|Dx2Jo(dt3iY}(Qo3x_P#*l~c`c=)Q{C0IC-cD<0IRF&sh%*03Fy#x#nWpJC zp(UCiT#7_Bzwq%rRRb@sPnfI_VmK8>v>^|o7_}j#eS7KXIHsUh#{;PU1#JLN`BeRPULsa4--AOxi2_7ndBHsUk$tRF=>ONZb+mJ?D z<06$wlL9z;J>27}S9l5q^UtGT;d$iEkkXAmL}ejla4VJP5)zvaA(iJ{+}b%F__+1; zLtGi816h>~e=m|7F}8LW63C5Ja+|bw0XdW6{tsDPJNv{{8n&|XX7BE3#zX+?;LCkeC+!Ap|7-x`hO8#)bLj^gul@> z1Kg|=$eX$w)fb<&hL#$+l}h;Nk9*5f+>Wf1cOiFz7H9TF3jW(DU;7XhiyxtS=`&QW zerhAXOKD{?_MSs{{EoADg1WjRMq~o;NoiQJ8u?i z`XQ8Fd=1r?UqV99aU1=;g`7HAS%c)^q6;=7puCgTx!Fdf1sB&*fGceld~k-Bf>LW% zKAI3;Wvx-PzME6ia7|Psw&#?S3XC9)yw`nTneWwVW2!f%rej+He@Dw-H2we@F1&-B zBR5fCi){q>b`Py@(=KEjTBb$64H<`TK*oV97GGC1CH3GXWFI<<#EyfA>e@p-PN+S4 zhbh;%jzc`x7M@of!g9;8F+SMsHx{~xWGVjmJZj*_3=4EMzS@UFa!vU6|2bNVK1Eh6W4=OD7C z9l6JrX>m^>uJgDhs#tEnx&zUQg?sC8O+o_UdbthuRA(e-&m>AG?uK_{jS3(WKiaVa z(S-V%y^cKAda)@`OGyy^j{? zDa$ykq%hR1e2(&&H{m%-6pLtJPOafj=VGXS=#Q_ykhe178IU5Zw*7$qb_yN8mY>SgxI$c$}W=k zH{d6ckp}VO8unVBgPaq$qx#&-D4)6oThepzqo5!pl0z4ryq5~>ebk?S9nQ{SY|qTc zMn?*1Mm68_9BL`tb+aGBH~u&xax16+f~f!sps`n}L(3?cdI{BwpYeLXAPS-qn12!J zqgPF7sx4T-+2@hF_$bmyo#IFpG`lghpaEg|^$1Nb#D>VY-ztEn!DEM;_mBRf;qdG) ziuXfrNe~QA1$E@r(U$CFbAe&!jAG?Xq;|wCaHEG+Mv9J|wM;d4eL>@S(O*|Brl(qEl?O+QKqeB^rM zkE-PsmB6`=Q8xcN@=x80Nb6M8!M{5V@0t72xcUl;hWT?JQ3pAFPJJ6Hx&{!EoIVrf ztw8qSMa#BxpLr0T<)@J|^&rwnN8&s7Bci1b*+*B9e&jMT3C(j(-ieCU7g0`Ln}7Na zTJ~oAm;$~fDI3Y;$XN%^nU$}<{ykLA+-5TfeiXDF4qqqA#~(udg)h;1=_6$9oJS0y zwT5+UFRbEwuk!p~Mb+$k@bOwB@_qP8NEkvqrHF3WiOfS6Q8xD`s^>mK{oGfmo&VhC z3TQr{5~@^oU9|Q*@7oJA`l?`7m1 zzZ2DGUq0_$si{n?-8*0%lY z_}ZPw*gZoea1rSTFIxv3HN;RZe9q|yP)G$(H1~pK3?}WKLu~hPq!Bf^lqSr-SB`w? zxi?Wl3tBY&h~?OCDJtUzJcg>pr_gx$GYYIyQ|~5UzJN3;5Ow<9kyVJ`oP5M|~K-_%`tYMITdl;F z$_%cZGbo*V7M17TK>qNmjYZ_Dvg0iLl4 zNZG>cR2&q%!fIR-9*OXxYQ&JrWKuDf&A*KrDuA|`Z_zmYC46%q@xDBd3}qFnfyl(Y z$Uc3mRoI1-3I%&At?$9c1P3<6#8L2z;Tf7i`I(1NvG59!)$1r-e#eBqtIPq3)95sG@eHb)H80&RL{UurrTc&&_-{ z@+KeiD}d!!QL_B9b z3H8@~gtANTB762>%RWru`zH_1B6H^`QreXy(_zC*Z0`T@o2dluL*?c7XbGR8Qn+ko z%XvZvw(*~&fKX88Dd1F2alU?202?D?5mw+sEZ?VU=^1!upD_AyEv@d4gF^^?>rg;$ z-FoF~6psIt6yP+`NG*O69$|ifq~4?ajQio4dWcGG72DlKxHcjRQRPk6kvQwbEhwLR z3%<$sQG1#+V(JUjEq*~|^E{FUr;#+Y0N3I3q)}%O+s^ARZ^YKzVg%=x!_~AK1w;jv zr*B8`)Z?gJc?Z4=pP=;Ohde&w@qzLA616a2{TStoA0U6?31l9=f}{aHkD&|j9J>bv zJa2{Z($%4O{4Tha0zG;s@)w@9&}{kA`>5ipi|3#* z^#faZH67>T3mV>u@;3cHW2?I1>KQ@i{__;FdufTEq?LMuyz^a47RuyCR(GAu(Oawy zSJ5gh5iN5bp|@w^9?P9q$6|HJ$rzePB^5~d+PhY)lSV6_LKKiq#h^wX2`z`svmd4K zWbbJccJ(kQoPU}Ec`yI=98wNkpg=!O3;hBu?u)2j`y5pZAE0FV86>wI!H>3W!!;E0 zt*%U)DOfaeJ<<=XBCf6v*F;A9xqMD3+y~F2`NCWL+%-$?i7KeThUhp8_r+AT!L@fD zNe7m#QOagl7B+-O8O2b(LDsR`P&xfB>c&4q)7Y12n*N#!fGA+;MH`+e&L7Wv7E9$9 z-Js#7bqFf*A*`YmiS0*`KDbN)eh5X2ub^V>eN>$L$Zz$}^E!Bxp8Ei0EAR9DJ~c?6 zJ^lcd;VsBHMkRdeVNxwB*fA>GQ+Fa`^mc2Asx)n*PAB;{vrke{KgoM?KN8yZAUdz` zFC)|4m4U~*zSh@&*-9z#rKOiu{eL3zE8uM2L!P=ofjNtUnFmpR@okjR!g!ZnK+5Qi zNT0YJIWv#KOJv|(dKKQ4H&DFtGTcXRKvaX@OP@?@ly{oEeeF&AyS%AKEIZAkT!X@9 z2*Tglv>6)-v(Yc9RX|gUHV`EQ zWfWSzd=in3ix7Sr6~Iq+gd#NG2Uq_Xk<9(5o&E%E<3C5&*e}sIPT`*>vY}#%?>_2p zTSh7sMOqQ6@MLj4kxi?G^HiUZ&^t;Lag_>y_iB~=eOU)-6Yr-|u!~k+L+Q%9C|!7m zsOnL8<{!6we(&5X@Xo(TB=#uL)m3Di3JCu(D$v8KR=kAt{iJNiZ$jn~BBhdB8CuFIr|Nl-$dgLU!d&5+i=f3MBaO=pZiYT zPh{{CiqF1782dH~$y1eQpQwbHfpNs|pG3hTQ3N?|*~+WPJaH?sMyUXZ03zxJtl&pI z=%VTd37?4wnsy<)ai?{sO=vr4Q*25WogGQue|{YqyZ>4NY>tk$ zv0&kRzc~JXIN|q?w{FAV*|G&;IYo%A?=`v+Ms6Hh(SnH5I&4nL#73tJ+p@ffui1la zB9`0(^9at!H|JqcW`X$?im8B_=f6Vt=&#T<@(VPMeU8eR4^ce*7&-qTgjKd7xTp@H zq#8j~p5dYnJbxRb)jVYV19FbvO=R<;-6!wT>qIm!Tg`Vd?9 zlD`U~@I+Ej-GuK2(@&vfiKNO_NfXvSA!>RZo|(s~WFJ80nS1>0&PMMr zUrFkL^GF&vjf9G3ILqr_uHU)$cha+AeXXzGuIw^j`DSNIO=y1Qdl@@v$xq)!%k?~k z{S8wBh0CuH_P=Qyzi{CN%QsiJP9?f2g9{cZaO_*K8FV?PAGCf0g%pI6wYQKv^&k>< zPavYfKY3Oos5IXom=-jWTs|bvhhQF>OII*@FZuBY*3-XZN(z7!VE%RF?w#{1fDIe5 z&6$FT@@C}jpF9h@n!FqbGtOH4uY=gc4Gc64K3J4EDYH`MrMk4>+%T?)y2< z``q(>&inZ^I$QOzzDENRW9v(!_X9cxMeafP__Yt4L#4tWYGUMtA5iqvsY4U{(%R~0 z3-+(&6+B(^>)6s~@cL7mzTja-bxV_f#h0sxn6SqRz^Ob^h*s0RF(1C*j9k z_lg$AnArYv{+z}F;%T+H5b;7%^u1MIZ+UiM^dn)Qui+h5lok?2BPlm|4cUb?1;}o zH|$6Uso_GxEpnOKza3)?tMh`_n0^%&oarwlcW6Ju3YD2Gk2UCK+KcQ5^dX5n|J6GnZGdn{JM_3mT`*dQ{41aa-TVtTlpkNwAPK zr%X@@Ql5+L+j^FFI=qRQ)v*KZ3SRhf-1V z13R9@&nXI!4pcN#AnW>)+AU^qyxWq3hF*mv`SFLJvzm1{W--v`4PKbm*Zfs>>mQF0 zQObZos1S0AC*<6|Xf`Ff&GqjD&xMOL5wKUIvPHWYu zlJ9v>m@~S@?aWxKM2hvWlq`kfv9Lh0c4*sOsH|Ytdpw$hnW>1aJ-Oo3b$b`EeQR6- zyWy;~Y{pjfF(1+tuCsJb1-Zd6zAFZWtUjC=+e512Otpe9$Issq6;}!8Csf^8R3Y6> zAtr&waUh_pkzkf9|zl3c90mKLm@+Xsh(t(9+Xl(ev(NH>R3`Z==>PNnnj^}~9{nj=yhV~v`~ zqW3mEbYOvuW%H-5Cg>pYjo)p>nJ}%cVxUWvxbsK_6cI}8$pr7{lN)$$hf!un4Cn!7 z6iy$Fi)hM+1NICdOP^JNE=f4slXDrMQ=f(3hH6Pg`9W@;sW&RovqTQ1^BTC8$cW`w z_A#Y6M~7>Ck%Q2jFzUM(+k|`qC(lHb2)0H%{kZ%Q5ojGH-{Wbd z(36Rg5fbUQ?Xnm+V;gLTZBb4Z&bcyNyUFRpDv1_juT|4vfE{OV+bA}g9TryNf0b#w z&@)D}WxrRVy-f>TDM*CJvTJFlli*Q(Q*wbx)A0k*BfZM@c(NMmk%LCxa!A#)TL|y5 z^K4fAu-hV1eb3abH5PoxXC4YRNBxQqF?zcN((6kNkyV3_Yk1yOnzIRH&M?L0haT{o z$%JZ}Z7cR~dd&C2-1-`Pa^2*n4H5qPzw-)x=SCJIORW}!&u$>K{Xofp?`DT~pF_Pgj^YLt>gW={b0<}& zz4ENfgxQqns_E?k_xIjtp71|RFGa6ue$(tc~x8e{^uD@wg7cCLJ()b#A8 z25PnPeWN_x!U*@-MKtV$3f$>SC|V$9x123rDS3Lp5_ln3T^Mc1Xsib~j(7aABf!G0 zshnLxTMfoEo#s&7j7;jW*ToI^_BbS3ihT!{UC8NzA-L(u^e}L!9Gjv`g*Y9f+g)TjK|IHw)Lu+E5r# zu6sl+W^-++)H)S1v%0bbwF>3PeeZn7sJ3&X0pk)>$aY?=B^)Iw!0dINU*!Qf4dw6h z+Ve?xzcq$_0b@-$F*8YUm$wimT9(Q>?|BZSF3r9=Dgz@LCHHc-L(uv>bEOmet39Nt z%#{OyXJy_a)tNL6Pk{Xs>?8v*VP!rvmE4jvX}IXePhLtoxCb(VqAuiOX#y|UGo^Yh zIL+ra@s@@3LCU<&7+8lm(UsR=d0ef0`!Qj4ZHv`U+LJNPKKJVuhwFle_u`kX5pUq6EAKK6y z*mxXy!maHE`O?U(c;1A{a5q>`SgbKTUF(wq8{p~l-Qk1H*_`l+_WBR16}m@s)Ni5^*AMtwU3N*sliDouaJ|=+kzS#lxt5gqo(!jnMCx7c+s{I!MK%O^WqSscTAOWq%WvQ@O(xi$E!~b+aJarU2=Kp66nWP zAf=SZqzMS9?hH~Z1WS|>O6mM%AZ|~%yIP%76h9n#=_fl_&Q8j2XZ5VqPd@}5r_we! z0dZ^#t9YA#)HfP|qR7=(GC$@Q(b>#Z*Hfl%xR)F~Ja-+Z_T5}!Xu`el^H7426Vfqw zvupzW&TZTzGys%-uU2_Pd^g7A9s|a0+{>(r^F%Q!Uawj zJF)^G<-2bdWEGO6KLtGnCwP>ECc6_RkBsm4sh$hT$}>7di9_? zSGY)?1>Bkb{$dDF`F7HCVm5JYVn;hfK9MI&d%fqTsy^i|+H7_ytOA@nIn2yrE-N05 zW&`eEM@|-;(t;7F1V~t`g{O2ByT#pb+&W{+&eA@uMIdOhiETTlGfsSQMk$n|Oys8U z!l<8)Qe6aB0mDg^NiZ07r+CMkz-Cvm61QGCEhqMk3ha94p3WTD+J+2On6#Z;^HLh0?P#F?FUlKl$xJvX3uZWEYlnJJmQ73F!rz=5CXLcuILbVWfp>fTBN?s(1 z=u9X+D6G;L4vho}x0G@iJFUl6FT?M23X7)nHz(P?6^PX}qjLRO#vR)=zf*Sh(ASA3 zw*~yF+gjzRYQ|^9hkRG(G- zw&7o3@EB~#W3fUH*i|ColUjV$k=W>>O#9DD8oVpWiar}O^b2ic)U=gtKIp^;NSUcM zh*Q^o%31ERnipgj+meeV(6@^mpWxqd8_TOkkyRNkd3N~w?f2qhYmeAXbVTO9>D=-` zc`|2r14(3c_1HecL-kU1lO3T*%yPZ0g<^B{ol8bg(W9MJTLm7XP?k}f>$+dUzO!H( z?J|4`t@cOI|8?;5Y|0jk1+CEy3d4`jFB6$3*18&sa^DtMM_nMjy+rQ37QI^h*gEjuVACM_zI%B<}xZPC?h=;BTG=(r)S&OSjr)u+9%2SYNooL=E`1HF7MkYWB^p z8ecu;LzOtF|LD<&W{dG~wX!KbV6%SY@S{g0ZdJj3F!pA#lVhQgX46C#)v5hXk_ce%$ek>ztJ;><^hnU%A6(T(6D(S*vzb!_==f!omXlK=dRsf3nm$u_d^ z%^kO&82y|8Q)2Huo7@qXNEYVet7AYbAWH9TBYQ?*xuVUCjCc+S|9zuDR{^d+iGCrc z>x=W(nc60@!?Wmrn?eboMSgohTUBg|_n)gioq{#~wo}C;Gj^fTpqYD`4eRm}b0rV0 zk*CSVao5?VCD_ zz#Vci1y=pjDVdv}z-|6&Gl z=98vS1&vsvD+g32{(VB#_iomke2>(j_MeBM36XaZiVPUT=ah?CIU!2_KcM;G_>ZOT z-p*vWMkULkyqx!_y7gN*NwCepB!eXG_FOfi8zQ%9v$A*7k~X>ORU?DA215zWC?+GJ z*Nn<0j_&^C*y``PzYJt*6ODg;lXFrLY52RnUct17?XCPZW*e2DxJ?Br;ug4QBU{T# zm$u&Muy=b2bYI9v$Ziy~e9Pt0jnv)UV;Zl(TWj6-3jdEqG)2rtj^tC_w zLcI1XuG}5&I^r$*bECsQfa69Zl&s0X%bE68ZYrLIQFj!xsAZ)kz7vM`qyLNSym383 z*Ohk) zYBRcBhL+)fd}iqJ_)UyR!;FKwcVmrw?}G>U2G>)ElC!*}R5wHw?ohk?6s6J>y%JRC zj#L6^oYT`86$q^Ow-WN^#<|jL`2a&O7=7`Aqc%n(^!A0hiFv(GXSA|@>{RZQ$2JEieHqxA>Vu=sa+dQa{?6s?X=x`A)gO@SG0G)u;7V>+7{y>!26 zxmbT~2uIrdBLH>_#Dm}C^&b?-qPBWM7kKB=q&eTpDNbrcW?ikV5xdl1Kqtm>ca_Nz zcpt$5DJfi6oUVSumFbw5nr(nmmz`#`Qbc5eC(b=CKDNIBnRAQ-2pMiu^J?SUL@GHS zU)9Aunwsm;NToq7#n1ZfK&anx-(aR6K%}(;tSG|<@e1FCCd;OCN=rBz`Kx89CyAZ$ z_(ssw3I4iUR$OdDi^Tr}UVfFMb~kJ+{bfYevVePUcf%O9E4fBep5KN)>03DL|0ZK; zp!)?j#+4R#SrRdt{VG8yfV$p@ny%Oxf-QHoqptk>ZuYtF-eo^2CW)6p3F}@|D|fym zrPsBs2`F*$9@hSg7@MEsuiU2fYLGi*b2`0%pnlF|Cqa#s9Q-dPaC;M~d_dJP+a*Ts zZ)e4>hPUJXK?}CGAR|+)FS7%AUoDW7>Jx=+UZrURlHp%&_&4z1InV4nBM&HNt-6aO UIqC1CsDMZJwt;4~hGWG400xhX3jhEB literal 0 HcmV?d00001 diff --git a/Source/Core/Resources/ErrorLarge.png b/Source/Core/Resources/ErrorLarge.png new file mode 100644 index 0000000000000000000000000000000000000000..1b1368fdacd947e031adc4b727fe455418a56fa9 GIT binary patch literal 1016 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 D7fsBI literal 0 HcmV?d00001 diff --git a/Source/Core/Resources/Filter.png b/Source/Core/Resources/Filter.png new file mode 100644 index 0000000000000000000000000000000000000000..334c89b0a3ae3dbb7ddab713ccbb238d7db6b1c8 GIT binary patch literal 481 zcmV<70UrK|P)pF33NqRbVF}#ZDnqB004<9jRpV!0e(qDK~#90-BCYl!f+5Z8WL1Q z(H7#Mpa@34L9xZpASHr}i#j;Cb}!QJAg0B^MFepbL;?;@B9bgtaB*-dsdRAc>-~OU zp|$_A_Q7$tym#;3xm^DSNTpIxRTY^`hWB(j%`2Hq@=7EUh{xlI#bQtt1s;zFvMk>o z1)ds>25j5L;c&oyzsGL3!)CL=a=FBOK8Il#7!HT%bUG-NN{}Q8!C>%)v)yj@H8s0l zuVcI2V!d9&G)*iP3(RISjK^d2`+exT4o%Y_isGx^@7KfOu=0L`AP5?@=US}>%d#+? zPBEEGFdB_Gx!rDaGPCaWdha8V$T`g)gCHkG=A>LMbK+nyK(E(3+04i^3ty&rbW$i3 z(Cv27YPC)_6Zz+tkVui-yi}`IG@DI?LLo+eAoAI_o4GJt}VjvKZSrH_s4*rBU XMp(DLU%$Y|00000NkvXXu0mjf<#W>N literal 0 HcmV?d00001 diff --git a/Source/Core/Resources/Folder.png b/Source/Core/Resources/Folder.png new file mode 100644 index 0000000000000000000000000000000000000000..1d415fd6ef824cb38223b86f63ffd149c99ac5a6 GIT binary patch literal 394 zcmV;50d@X~P)5j;R- zWga225_xrztxMHaF>yT^iOirXS0a%ok(H5^$jZFI-y{SMN;`w1Z8Wi5qo0+f%HqvP98$@9vn}GU}T?3jM zWOn-?lUPrZrfKpt@u44WPF6|6XnrSLE#f+fx~^9ZvOHXEKC~L$MI`Vp;ja}&5_mm7 zpe)O_h~xNt=Ox|gm8d3hsFLh^0rSGA^t1zeliiThS0V&Kq~OSQsK9tv3^N;jo%-)8_`(D40t?OK2sb9xKRGh3Z~F o2oAwH_#Fdd3_Nh?7yq;R0wW>6@5A6eJOBUy07*qoM6N<$f|-V+-2eap literal 0 HcmV?d00001 diff --git a/Source/Core/Resources/Font.cfg b/Source/Core/Resources/Font.cfg new file mode 100644 index 0000000..8a0f9fd --- /dev/null +++ b/Source/Core/Resources/Font.cfg @@ -0,0 +1,710 @@ +count = 64; + +chars +{ + + 65 + { + width = 26; + height = 42; + u1 = 0.011719f; + v1 = -0.003906f; + u2 = 0.078125f; + v2 = 0.195313f; + } + + + 66 + { + width = 26; + height = 42; + u1 = 0.082031f; + v1 = -0.003906f; + u2 = 0.148438f; + v2 = 0.195313f; + } + + + 67 + { + width = 26; + height = 42; + u1 = 0.152344f; + v1 = -0.003906f; + u2 = 0.21875f; + v2 = 0.195313f; + } + + + 68 + { + width = 26; + height = 42; + u1 = 0.222656f; + v1 = -0.003906f; + u2 = 0.289063f; + v2 = 0.195313f; + } + + + 69 + { + width = 24; + height = 42; + u1 = 0.292969f; + v1 = -0.003906f; + u2 = 0.355469f; + v2 = 0.195313f; + } + + + 70 + { + width = 22; + height = 42; + u1 = 0.359375f; + v1 = -0.003906f; + u2 = 0.417969f; + v2 = 0.195313f; + } + + + 71 + { + width = 28; + height = 42; + u1 = 0.421875f; + v1 = -0.003906f; + u2 = 0.492188f; + v2 = 0.195313f; + } + + + 72 + { + width = 28; + height = 42; + u1 = 0.496094f; + v1 = -0.003906f; + u2 = 0.566406f; + v2 = 0.195313f; + } + + + 73 + { + width = 13; + height = 42; + u1 = 0.570313f; + v1 = -0.003906f; + u2 = 0.611328f; + v2 = 0.195313f; + } + + + 74 + { + width = 22; + height = 42; + u1 = 0.615234f; + v1 = -0.003906f; + u2 = 0.673828f; + v2 = 0.195313f; + } + + + 75 + { + width = 28; + height = 42; + u1 = 0.677734f; + v1 = -0.003906f; + u2 = 0.748047f; + v2 = 0.195313f; + } + + + 76 + { + width = 22; + height = 42; + u1 = 0.751953f; + v1 = -0.003906f; + u2 = 0.810547f; + v2 = 0.195313f; + } + + + 77 + { + width = 31; + height = 42; + u1 = 0.814453f; + v1 = -0.003906f; + u2 = 0.890625f; + v2 = 0.195313f; + } + + + 78 + { + width = 28; + height = 42; + u1 = 0.894531f; + v1 = -0.003906f; + u2 = 0.964844f; + v2 = 0.195313f; + } + + + 79 + { + width = 28; + height = 42; + u1 = 0.011719f; + v1 = 0.183594f; + u2 = 0.082031f; + v2 = 0.382813f; + } + + + 80 + { + width = 24; + height = 42; + u1 = 0.085938f; + v1 = 0.183594f; + u2 = 0.148438f; + v2 = 0.382813f; + } + + + 81 + { + width = 28; + height = 42; + u1 = 0.152344f; + v1 = 0.183594f; + u2 = 0.222656f; + v2 = 0.382813f; + } + + + 82 + { + width = 26; + height = 42; + u1 = 0.226563f; + v1 = 0.183594f; + u2 = 0.292969f; + v2 = 0.382813f; + } + + + 83 + { + width = 24; + height = 42; + u1 = 0.296875f; + v1 = 0.183594f; + u2 = 0.359375f; + v2 = 0.382813f; + } + + + 84 + { + width = 24; + height = 42; + u1 = 0.363281f; + v1 = 0.183594f; + u2 = 0.425781f; + v2 = 0.382813f; + } + + + 85 + { + width = 28; + height = 42; + u1 = 0.429688f; + v1 = 0.183594f; + u2 = 0.5f; + v2 = 0.382813f; + } + + + 86 + { + width = 26; + height = 42; + u1 = 0.503906f; + v1 = 0.183594f; + u2 = 0.570313f; + v2 = 0.382813f; + } + + + 87 + { + width = 33; + height = 42; + u1 = 0.574219f; + v1 = 0.183594f; + u2 = 0.654297f; + v2 = 0.382813f; + } + + + 88 + { + width = 26; + height = 42; + u1 = 0.658203f; + v1 = 0.183594f; + u2 = 0.724609f; + v2 = 0.382813f; + } + + + 89 + { + width = 26; + height = 42; + u1 = 0.728516f; + v1 = 0.183594f; + u2 = 0.794922f; + v2 = 0.382813f; + } + + + 90 + { + width = 24; + height = 42; + u1 = 0.798828f; + v1 = 0.183594f; + u2 = 0.861328f; + v2 = 0.382813f; + } + + + 48 + { + width = 22; + height = 42; + u1 = 0.865234f; + v1 = 0.183594f; + u2 = 0.923828f; + v2 = 0.382813f; + } + + + 49 + { + width = 22; + height = 42; + u1 = 0.927734f; + v1 = 0.183594f; + u2 = 0.986328f; + v2 = 0.382813f; + } + + + 50 + { + width = 22; + height = 42; + u1 = 0.011719f; + v1 = 0.371094f; + u2 = 0.070313f; + v2 = 0.570313f; + } + + + 51 + { + width = 22; + height = 42; + u1 = 0.074219f; + v1 = 0.371094f; + u2 = 0.132813f; + v2 = 0.570313f; + } + + + 52 + { + width = 22; + height = 42; + u1 = 0.136719f; + v1 = 0.371094f; + u2 = 0.195313f; + v2 = 0.570313f; + } + + + 53 + { + width = 22; + height = 42; + u1 = 0.199219f; + v1 = 0.371094f; + u2 = 0.257813f; + v2 = 0.570313f; + } + + + 54 + { + width = 22; + height = 42; + u1 = 0.261719f; + v1 = 0.371094f; + u2 = 0.320313f; + v2 = 0.570313f; + } + + + 55 + { + width = 22; + height = 42; + u1 = 0.324219f; + v1 = 0.371094f; + u2 = 0.382813f; + v2 = 0.570313f; + } + + + 56 + { + width = 22; + height = 42; + u1 = 0.386719f; + v1 = 0.371094f; + u2 = 0.445313f; + v2 = 0.570313f; + } + + + 57 + { + width = 22; + height = 42; + u1 = 0.449219f; + v1 = 0.371094f; + u2 = 0.507813f; + v2 = 0.570313f; + } + + + 32 + { + width = 12; + height = 42; + u1 = 0.511719f; + v1 = 0.371094f; + u2 = 0.550781f; + v2 = 0.570313f; + } + + + 33 + { + width = 12; + height = 42; + u1 = 0.554688f; + v1 = 0.371094f; + u2 = 0.59375f; + v2 = 0.570313f; + } + + + 64 + { + width = 25; + height = 42; + u1 = 0.597656f; + v1 = 0.371094f; + u2 = 0.662109f; + v2 = 0.570313f; + } + + + 35 + { + width = 22; + height = 42; + u1 = 0.666016f; + v1 = 0.371094f; + u2 = 0.724609f; + v2 = 0.570313f; + } + + + 36 + { + width = 22; + height = 42; + u1 = 0.728516f; + v1 = 0.371094f; + u2 = 0.787109f; + v2 = 0.570313f; + } + + + 37 + { + width = 33; + height = 42; + u1 = 0.791016f; + v1 = 0.371094f; + u2 = 0.871094f; + v2 = 0.570313f; + } + + + 94 + { + width = 22; + height = 42; + u1 = 0.875f; + v1 = 0.371094f; + u2 = 0.933594f; + v2 = 0.570313f; + } + + + 38 + { + width = 29; + height = 42; + u1 = 0.011719f; + v1 = 0.558594f; + u2 = 0.083984f; + v2 = 0.757813f; + } + + + 42 + { + width = 19; + height = 42; + u1 = 0.087891f; + v1 = 0.558594f; + u2 = 0.140625f; + v2 = 0.757813f; + } + + + 40 + { + width = 13; + height = 42; + u1 = 0.144531f; + v1 = 0.558594f; + u2 = 0.185547f; + v2 = 0.757813f; + } + + + 41 + { + width = 13; + height = 42; + u1 = 0.189453f; + v1 = 0.558594f; + u2 = 0.230469f; + v2 = 0.757813f; + } + + + 95 + { + width = 17; + height = 42; + u1 = 0.234375f; + v1 = 0.558594f; + u2 = 0.283203f; + v2 = 0.757813f; + } + + + 43 + { + width = 22; + height = 42; + u1 = 0.287109f; + v1 = 0.558594f; + u2 = 0.345703f; + v2 = 0.757813f; + } + + + 61 + { + width = 22; + height = 42; + u1 = 0.349609f; + v1 = 0.558594f; + u2 = 0.408203f; + v2 = 0.757813f; + } + + + 45 + { + width = 12; + height = 42; + u1 = 0.412109f; + v1 = 0.558594f; + u2 = 0.451172f; + v2 = 0.757813f; + } + + + 91 + { + width = 13; + height = 42; + u1 = 0.455078f; + v1 = 0.558594f; + u2 = 0.496094f; + v2 = 0.757813f; + } + + + 93 + { + width = 13; + height = 42; + u1 = 0.5f; + v1 = 0.558594f; + u2 = 0.541016f; + v2 = 0.757813f; + } + + + 58 + { + width = 12; + height = 42; + u1 = 0.544922f; + v1 = 0.558594f; + u2 = 0.583984f; + v2 = 0.757813f; + } + + + 59 + { + width = 12; + height = 42; + u1 = 0.587891f; + v1 = 0.558594f; + u2 = 0.626953f; + v2 = 0.757813f; + } + + + 39 + { + width = 10; + height = 42; + u1 = 0.630859f; + v1 = 0.558594f; + u2 = 0.666016f; + v2 = 0.757813f; + } + + + 34 + { + width = 17; + height = 42; + u1 = 0.669922f; + v1 = 0.558594f; + u2 = 0.71875f; + v2 = 0.757813f; + } + + + 44 + { + width = 12; + height = 42; + u1 = 0.722656f; + v1 = 0.558594f; + u2 = 0.761719f; + v2 = 0.757813f; + } + + + 46 + { + width = 12; + height = 42; + u1 = 0.765625f; + v1 = 0.558594f; + u2 = 0.804688f; + v2 = 0.757813f; + } + + + 60 + { + width = 22; + height = 42; + u1 = 0.808594f; + v1 = 0.558594f; + u2 = 0.867188f; + v2 = 0.757813f; + } + + + 62 + { + width = 22; + height = 42; + u1 = 0.871094f; + v1 = 0.558594f; + u2 = 0.929688f; + v2 = 0.757813f; + } + + + 47 + { + width = 10; + height = 42; + u1 = 0.933594f; + v1 = 0.558594f; + u2 = 0.96875f; + v2 = 0.757813f; + } + + + 63 + { + width = 21; + height = 42; + u1 = 0.011719f; + v1 = 0.746094f; + u2 = 0.068359f; + v2 = 0.945313f; + } + + + 92 + { + width = 10; + height = 42; + u1 = 0.072266f; + v1 = 0.746094f; + u2 = 0.107422f; + v2 = 0.945313f; + } + +} + diff --git a/Source/Core/Resources/Font.png b/Source/Core/Resources/Font.png new file mode 100644 index 0000000000000000000000000000000000000000..c7986dcb6113311232afd1d744c15fdedebc5967 GIT binary patch literal 20550 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 a0R{lhw!)b<$Mui^00002BR0kYI^x zM2T~LZf4nJa0`Jjx$jvXuz>w*jR$=RM<}lD622WQ%mvv4FO#sk7Jd*$b literal 0 HcmV?d00001 diff --git a/Source/Core/Resources/Grid4.png b/Source/Core/Resources/Grid4.png new file mode 100644 index 0000000000000000000000000000000000000000..039cfdc394c203fa0d56cc609f8d3255a5bd7dc4 GIT binary patch literal 301 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`oCO|{#S9GGLLkg|>2BR0kYI^x zM2T~LZf~>qyY6>&2#uZHBFl)7FPMifrYU;Ffj0o1JmcmHiH99 z6X(yDHx`)qQ$hOT)5diNRr_t%-Mn$5;L}FQ2Ge^BxeT8;CN^K^K2Z}fL%@f<H@xEa94@aXm;sb=%J;Xq$8c)I$ztaD0e0svxYcP;<` literal 0 HcmV?d00001 diff --git a/Source/Core/Resources/Help.png b/Source/Core/Resources/Help.png new file mode 100644 index 0000000000000000000000000000000000000000..cd784fbaa6452557103bfb10d094ef043469b15d GIT binary patch literal 367 zcmV-#0g(QQP)Yk zNt>n}lg|4oP2v4teja@c6IG$w)8mdT*TbgF%heJ_8R$-_es;_p%5*vev14Wvpizy7c|b7Uk;241h!nnuW@{~W&$|dHRt3tk#CpCSfl}tP z(_g$leup8l9 literal 0 HcmV?d00001 diff --git a/Source/Core/Resources/Hourglass.png b/Source/Core/Resources/Hourglass.png new file mode 100644 index 0000000000000000000000000000000000000000..94c428c385466a1e10b7e7a9cd4d8b5a88ccf998 GIT binary patch literal 507 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=oCO|{#S9F5he4R}c>anMkYI^x zM2T~LZfFSZ)X_hH9H8nerKAZr?d1agN#C|$i?NpjY?jt z?j)#|X9iz#ooK-j6jaoA=YXub!ms4@t+{TV%-J8Gdn*6VmOh`nm&YQVm6-dL>B5&O3?}`1su)uvy*Ah0_ddL^Qsmk-9fllzRUec6u`jl}NVi;A zU?RivV7K+1U(>SWT;{U)TPq%#%75fGmdK II;Vst0FZplrT_o{ literal 0 HcmV?d00001 diff --git a/Source/Core/Resources/Hourglass3D.png b/Source/Core/Resources/Hourglass3D.png new file mode 100644 index 0000000000000000000000000000000000000000..49028309473364f42be38dae7727900d394ed09e GIT binary patch literal 463 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=oCO|{#S9F5he4R}c>anMkYI^x zM2T~LZf555*U zUedcxH_RdSdgA@>KQw185dy!z-Z*`s<(9OSJ6}ib)bZ@tsj?{U)Ia-aC%iDcr5it)0vw>MztKzToeE zmY8ok3xwmpmL0tO;Ns~C%3UHK#5s?0{#cLQx3~l3Cremz&2;8nw^f0W!QkoY=d#Wz Gp$PyDa=6<7 literal 0 HcmV?d00001 diff --git a/Source/Core/Resources/KnownTextureSet.png b/Source/Core/Resources/KnownTextureSet.png new file mode 100644 index 0000000000000000000000000000000000000000..36c25b5c87276ace2303c585c6ebe9fddb620918 GIT binary patch literal 476 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 literal 0 HcmV?d00001 diff --git a/Source/Core/Resources/Monster2.png b/Source/Core/Resources/Monster2.png new file mode 100644 index 0000000000000000000000000000000000000000..7542cfedf91c4c0c45e7cabd15021180446a6935 GIT binary patch literal 880 zcmV-$1CRWPP)E-G+zbmSbZs`@S#i(~3)j?XjB&MT6aEJ<(T@jB%!mMoNE z6cvX`=fdUkEd>Py9~YTLB2lJ`t1BfHiH`OU4`ZUY2h$P>zJ!F}n_7+c>(=2_R21%w zSYhtD08g5hdU$y7?Etb7rRU`In)32y8Z{bpb#&mdOor?J{um+}5ehM$lmt_2D@-aC z8Y(NNHz<{hdn{bO+$w>x+q?AWVwIXk57xGr_^QMIdL%`Av}swz>odhdR5N z$EN(+7-r$7L4^Xw1QkpXk*M=$P$gRpqkutctP+iC6|CXm3w60PonZ%$JiLER1mcIz`?kcZ*y8o_VCP8Y}8*W}quyV4LIvl$V}{WzSR z1*0Ynm$D2vL2^+}4&tLzkge6>nqCL1Q1H|a@FY4Hy!OYVZQEd79}1mMG)|c>A~CxQ zdrNDf$+h52a|h~$K^RrV;(=6#WA5&wc7QjV9a<$6zPYN^Vk{s4Zz95Aj#pq*9gmlB zaTp}IJ2o0`$qU?1N`#qVrc#}peC+@>(JB@zptZallVtjA19XRg?h(Brt=Y0|4s3ct zW*}dy&Gi2Bvrud{D%G7fbLU*x~8bLm=Ic%|o10C!T(19BrLrnLk^ce6WHJv!p-}Ns zZ!bP?Z)0b25?|eJ{P22F<$3%Z8Nr5Ryq-+vG|B zcQ@7?4&2aauxzoQ!twB$&3M*nM5!pEKN?l-1Ed&cFHm7w3{w=QOeSpje7I%Qq1;KM zpx0s|*@JJ-Z)4VJL7t)(e*;!lRb1w(Tmf|dcq;&T|V_u(?9!j+I8nN|)3Er**L9?k|6CUiRVkS#DA zZiT&GhhUQhgud%Rv7Nn!NIn8z&WfSv5HbUUIBz=*p6o$yqaJj<4Qgc=v?PWu!*2fq zj(Y=z!aGXrPQx*oP*YRG?B0r#O2KNKMO)h#NYb@fTYHG9tK*9t_ZBQ$fn07HmC26o cdH>1$0%zemeL5Xi4*&oF07*qoM6N<$f}s#K1ONa4 literal 0 HcmV?d00001 diff --git a/Source/Core/Resources/NewMap.png b/Source/Core/Resources/NewMap.png new file mode 100644 index 0000000000000000000000000000000000000000..d8a0cc035bf54667d93bc56704ea744f32ca5017 GIT binary patch literal 263 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`oCO|{#S9GGLLkg|>2BR0kYI^x zM2T~LZfFS$kcv5LFWK@PGT>o-AlI2(z|(g{IpxiZi5HXI`(w~)g-zw6IlkCO&}iWzpka~53}!o9|mG5=J{*OGllI<{3Bn=zz!3GQ9R zEmEPEZ`dfraBJNOwwnw;8tz297%{wkd)?^1QNkw1-0P{&SR{BIc9lgLEs%Z3@N>)M zL&qD+t=GTxaBukcifc>ioc#>HzJC*9`kUXK#I*kmvqiwCoq+`#Sb*+j@O1TaS?83{ F1OO7$V}bwx literal 0 HcmV?d00001 diff --git a/Source/Core/Resources/NewMap2.png b/Source/Core/Resources/NewMap2.png new file mode 100644 index 0000000000000000000000000000000000000000..aee6681b50c9d5aadc8141798a27771d847a51cb GIT binary patch literal 329 zcmV-P0k-~$P);D3sgKGS3N|W&^<_G)OFNY)6{1SHwDDnozSuxCl8-1ax4r zZz@yla-q{|VmRp2r^Dh|I*}-5RA@;WRikTG-Q@^3zKM?ns3?%Q zL0#i7f~2ZhsRO9vpn@XR)!+YHZ<(^X96*u$cA!H$xa8@sg1{~(w)-Ov=gT)^-wB?t b?QeJh^)YVdGKolH00000NkvXXu0mjfn@Nyo literal 0 HcmV?d00001 diff --git a/Source/Core/Resources/NewScript.png b/Source/Core/Resources/NewScript.png new file mode 100644 index 0000000000000000000000000000000000000000..9e6fe958a65e09c5b5bfe7423b0f6c1db977d42e GIT binary patch literal 765 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 literal 0 HcmV?d00001 diff --git a/Source/Core/Resources/Prefab.png b/Source/Core/Resources/Prefab.png new file mode 100644 index 0000000000000000000000000000000000000000..54561df439c29d450fb57b72b55c71a5f4ead086 GIT binary patch literal 514 zcmV+d0{#7oP)u0#O)s=|&6{ z9LJGnP)P?76)hwbDkw^+Rj8DZQA9>Wfh84P7ezrq6fFvRP>aA#i)z~{+O&xN(P_Tx z1C?IZfx~^wch2|SGdf)hY#TNfzK$bu(+9cegpe0t&Y59M8KEE1v)1}Lgf)RVw_ONc z^kC>^0=|=OI96pSTV0&yDKkuyCUm4bU`QM8o5V^<0g3%*5hUMp=)Ft8Rq}EZA{kic zt+32mV42Z4ZGjGqUAwIg-&j2&08TX2;4v} zDsb=k_yqLLnbH1`rmF|(7<1GdL3<(vM&WS-Z7TI3$XBXq64!9cLSR`1)g!*)*QlAK z;>LP0-8G(nM5{?#;zpAOVSD0F4BE%P4O)Zz3x4(f4`R~u)Gbr!i2wiq07*qoM6N<$ Eg4kcxSpWb4 literal 0 HcmV?d00001 diff --git a/Source/Core/Resources/Prefab2.png b/Source/Core/Resources/Prefab2.png new file mode 100644 index 0000000000000000000000000000000000000000..c46beb6c2852ea1e826564102191e52fea93b10e GIT binary patch literal 552 zcmV+@0@wYCP)S)o8s_xdA+N|x z--;l0kwMqnFcQa22raA7HtV^(qh5GMJg6P4g=^6DSdpxiG*EK#v=P0ZV`zEEAySHS z2#Emx89#iJKKLdqjr-s#xW2myl)}t7YCz_`9l4JJvd?{p?>6v60@13>2M{1F1T6)i z%qiBH0?)LEN#<1+txvtky%*5&(vQ?hv)#6+&XX5|d?vA-Boaqy#P*V~>O-P`qd`S? z40OH@!8~t;x)J3GR9Pukrg`Wr2IIhlahQg>7WoEHgz_N-mx5@!>_PK=r}ZA@P=C=6 zc~<5CbyY)jJHb1kZ~BmRL)|iyT3)6=B1oCY-$^tu8XgB&T^ai+z6uYoo;b^MfO%)) zmYHA$%?4GIzNOoe=t0TiKtKkvZj^7CpOyEvk7h^W4(Fi*Qjsh?cCrXz68oQ3g=p{e q{T5Kw_kRFq{G7p`08-`ERgEtunl_gs*8EQZ00002BR0kYI^x zM2T~LZfCCscGN;v@YX5t0ueB;`!z24YyI$U6D>F=z zni1aXX8h^i@9WZX_V4?<=ga3cY`rIJdZ|m$_F%@ruE>nO&iuwD55@L~r!y&*vgl|> zoK{#KU}Bck9qsmIYI2|VUe4I7L6+T;C$*O>wb`!Pqo@(h&$f-Z#=K*4hpF(QBgt~U zQ<}`5@*P&qShhfHapP;DnCz-RG3jJ>;qwWL;zHu5Y3M(R5`AFJbY61n%77nwPgW|e zUCLW2xu|49!-b#sRJNF|+BC&Pr+M>@b1$Dh=KPwIG~s&Kp4fAihJhTbJ8JwqXUX3` zcqL)G*(8n0Y>%Xt^RD@9d-=EKb9IT;$2S(`X&2Wuos}1EG2nS2!zcBkl6jY!z}#mG f4%XK_J!AfO%7?lii+0ol1D(Os)z4*}Q$iB}wCb%4 literal 0 HcmV?d00001 diff --git a/Source/Core/Resources/Question.png b/Source/Core/Resources/Question.png new file mode 100644 index 0000000000000000000000000000000000000000..77275572783522be204d1392bd8f8a9e3b97977b GIT binary patch literal 960 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 literal 0 HcmV?d00001 diff --git a/Source/Core/Resources/SaveScript.png b/Source/Core/Resources/SaveScript.png new file mode 100644 index 0000000000000000000000000000000000000000..e5ac50d170b9e3576574e62bfd5c86e1b8d91f59 GIT binary patch literal 923 zcmV;M17!S(P)fkNdYmo+k-bLMr~SN-&}lmQ|v0m3Sgq8dWwJom)d>W>N+F zHI6;KWWP=du!vf)VALt+XXGq_0=>}$7{4Os(()?s+s zOW&}Ce&NE1g-fFr3=^~5^Cb}de0kc&m@A0L+({MWw3v^Nxk4-i6mGa=#_q}7ydUM( zyv&3I`(*U)xH35V2w9c@C~<=>96E9+FU;Qi{sXL9O| z#1b1^pNn#3I)L65WZ-;+wvHe^e>@o!^M@lci~cahRg-*HG?FaHJbLcgPwMqzgEr7j zdFi}rL33q_vsypRzbq37%734~!m@uA+q|FbQUm*PFC)EOsZA5UYP{fJNqlPe_&Nhq z>*(&vbhLUo*B_;&)x%FGA5h)kpuEP+-U=fh=9%~?=Pc4&Wt}eyfdh3qs(W2jb~-8V zn5FnvCyfmr1}{R>iN};ynkYUzNZ!F-@;)76PwogGWNDDzJk*v|r=9rUKd?FP_dmG! ztwZIzT01XqdzFHcBj|qrlLKX~yti|RjLefrZxps{t7taRW18XexSN}ISFpQ-czhA+ z>y~J2S|j`5MfMgQ=1jj4+w43a7aZgDceSK%Z$f%4zd23VA(RTWLZ@I7ocZ6FB41ZI x$k^3RLE&jiO1jx!bc)Q38s6F7KzjO7{sOgm3p@Iwaa;fZ002ovPDHLkV1mPhw37e; literal 0 HcmV?d00001 diff --git a/Source/Core/Resources/Script2.png b/Source/Core/Resources/Script2.png new file mode 100644 index 0000000000000000000000000000000000000000..80f929b99ba17b84622cad4d750dc23f06a7b573 GIT binary patch literal 845 zcmV-T1G4;yP))P)PmilPe%QqU4iB{a}*n{9@anVGgvKh|oR%Wb+3 zH@7yO)7j>})P0_v{d;!Ab5RF==fKPV`JMND&T|9+1O;k_e20miMokb;n+&~vhFI*5O--3*K14g+T>4a-IvdT#>n41Yj*H*uU7 zRLPc0di5c$$QR8a5MvRFvxp>E#M3NNSr*yco;1beF}=JCsQeV&x+Gl@>_VdraE5F}uoeN37_Y1?7thb-bYT z#l%i5nWML5Ah*S#ux&$S-G+t=)r8KS#?KXoYh?LLEhsPG1(%xSg3@Ls*IlJ03W?GT ziNb__xruug(AeWJ_!uNIIZ!@TsNn?{8$KT>Yf&PT%VT0Wh4FA(0y)Q`_kXS$IM$=G|MM zyo48=tCtlK58H-qSf}CX*C#P#Ok&KMhT4(Dlq&(9Cxu`BEVgK zaAort8U~!G7ddd}%@S_CcA)x|9hDtcRJ2et*NMBG3P@FUe9?QMvj$-CM&Ju35Zz^vq<^3= zJYE{eOge>DPC~8s96wGA<;IT|*FqW-{lmEl3(CzkTUbdGG|;j9kr2im zq`@pKoN#!a-63&_%AI!tM|!`wJ6?UD@%&-W_WI}hyx*_y_xbGmD1b7L;mr6`!qJ+a zJ?!mrQ_(R&`T4uZ+HGM#XcJn5gTgLhxu7VEf}L$+s}J>hc>H-3j^TM2!Ql;Ioe0tI z^mFR!eI!3Byr>EaTSqb*`@IXL@#!coUkvX|j46MNnNW=2Y>e6Hf6`!Nj^2?+Nav<1 z*xqc*J2~Jb5(BF%g3UdP-8IW!-UxqBMY!XOlJ$;`?VICyb#pNINv)&!H!ajv>Ha*71nIbqt7vgbgrp_+=hXJeGVVDCnNmg z2+?;d#O2W-zm0|1@j(^(%^Cs&|^L0*` zt!VWnD$ss*@u4pe<%%;*mu&`WqICR)00@?*b~%QswDol}f^{A^g8Ou_ni zs;l?llq)?k9pS*SKBV)qDtM*vaGa1SlnXUNlh7^L1%G<+5i*R8Y~9_0q4q4YW)hxU a6#NUK&cS~`G9~~30000 c #485c75", +", c #93a2bb", +"' c #9da9c1", +") c #96a3bc", +"! c #eceff3", +"~ c #dce0e8", +"{ c #556880", +"] c #f4f6f8", +"^ c #9ba8bf", +"/ c #dce0e9", +"( c #52667e", +"_ c #e4e7ee", +": c #a6b1c6", +"< c #586a83", +"[ c #ccd2df", +"} c #b5bdc8", +"| c #b5bed0", +"1 c #40546e", +"2 c #adb8cc", +"3 c #9fabc3", +"4 c #edeff3", +"5 c #ccd3df", +"6 c #e4e8ef", +"7 c #8b9ab3", +"8 c #d8dde6", +"9 c #f4f5f8", +"0 c #acb8cb", +"a c #798aa3", +"b c #909eb7", +"c c #d3d9e3", +"d c #d4d9e3", +"e c #e4e8ee", +"f c #fafbfc", +"g c #aab4c9", +"h c #3b4f6a", +"i c #d3d9e4", +"j c #b4bed0", +"k c #e5e7ee", +"l c #374b67", +"m c #334863", +"n c #a5b1c6", +"o c #52657e", +"p c #a9b4c9", +"q c #dce1e9", +"r c #66778e", +"s c #435771", +"t c #a4aeba", +"u c #5d7088", +"v c #f5f6f9", +"w c #465a73", +"x c #8e9cb5", +"y c #7f8fa8", +"z c #394e68", +"A c #ccd2de", +"B c #c5ccda", +"C c #a3afc5", +"D c #3e536d", +"E c #bcc5d5", +"F c #8998b2", +"G c #ccd3de", +"H c #cbd3df", +"I c #dbe1e9", +" ", +" ", +" ", +" ", +" 8p%^,xF*ya} ", +" g--f9!k~d5r ", +" C-#v._IiABu ", +" '#].(&mG$E{ ", +" )]46/cHBE|@ ", +" b!e~o1mEj0w ", +" 7+qd[$E|2nD ", +" =/;G$E|0:3z ", +" }usDhlt ", +" ", +" ", +" ", +}; \ No newline at end of file diff --git a/Source/Core/Resources/ScriptError.xpm b/Source/Core/Resources/ScriptError.xpm new file mode 100644 index 0000000..38a7d55 --- /dev/null +++ b/Source/Core/Resources/ScriptError.xpm @@ -0,0 +1,100 @@ +/* XPM */ +static char *image_xpm[] = { +/* width height ncolors chars_per_pixel */ +"16 16 79 1", +" c #00ffff", +". c #f33b00", +"+ c #ea3900", +"@ c #ee3a00", +"# c #e43600", +"$ c #ba7a64", +"% c #ff8b65", +"& c #ff7c52", +"* c #ffbea9", +"= c #ffa183", +"- c #f87247", +"; c #ff9a79", +"> c #d49b8a", +", c #bd3306", +"' c #f1ab95", +") c #ff6a39", +"! c #ff6f41", +"~ c #d19481", +"{ c #fb7950", +"] c #7c250a", +"^ c #fe5c28", +"/ c #ff5f2c", +"( c #ff8e69", +"_ c #dc9d89", +": c #e5b6a7", +"< c #ff9c7c", +"[ c #fe7f56", +"} c #fb8660", +"| c #fa8059", +"1 c #fd5721", +"2 c #f29679", +"3 c #ffa487", +"4 c #fa4409", +"5 c #fb480d", +"6 c #e0ad9d", +"7 c #dc957e", +"8 c #b9725a", +"9 c #bb5a3b", +"0 c #e23903", +"a c #77311b", +"b c #e35a2e", +"c c #b62f05", +"d c #feb8a2", +"e c #fed8cc", +"f c #f83e02", +"g c #7f2e14", +"h c #6e2a14", +"i c #d03c0c", +"j c #dda797", +"k c #fec7b5", +"l c #b27967", +"m c #fed3c5", +"n c #fb5018", +"o c #f94107", +"p c #a45f49", +"q c #f2d8cf", +"r c #ddaa9b", +"s c #ffa88d", +"t c #ff6d3e", +"u c #ac4d2e", +"v c #ff612d", +"w c #fefefe", +"x c #fd521b", +"y c #b8411c", +"z c #ffc4b1", +"A c #c66b50", +"B c #d48c75", +"C c #dc3602", +"D c #dd4c1c", +"E c #d4947f", +"F c #ee734c", +"G c #962a09", +"H c #ff8660", +"I c #933e23", +"J c #d9704d", +"K c #ffb197", +"L c #d58970", +"M c #e5a38d", +"N c #ecc2b4", +" ", +" e6_>~L~e ", +" NjM2}{FJA7 ", +" Nr'3;%&tvb9B ", +" qj'Ks*ww=vxDuq ", +" :M3s3mwwk/x4y$ ", +" _2;<;zwwd^noiI ", +" >}%(%Kww;15f0a ", +" ~{&[&Hwwtn4.Ch ", +" LFt!t)(Hx5f+,g ", +" :J/v/%ww{f@#Gp ", +" qAbxx[ww|@#c]q ", +" _9D4o--+#c]l ", +" Euyi0C,G]l ", +" q8Iahgpq ", +" ", +}; \ No newline at end of file diff --git a/Source/Core/Resources/ScriptHelp.png b/Source/Core/Resources/ScriptHelp.png new file mode 100644 index 0000000000000000000000000000000000000000..c319c003374933d8d332ea348a1550cfa03c8257 GIT binary patch literal 965 zcmV;$13LVPP)TXh`A51RPm zn-6=^2cs{W+2SRdXpGS~vzRR=&fAa?%rXra*v8`G!!9}$9NAQ4n8_xW4OnoS+y^bC zQdnt8TUwwNS_Tv-&_ZojF1^v7e*B-aWe@sKKL2yh|9sEyek4H3yR|ScDL28m*v|H? zB2LOBt@9oiHEGmWkr?>q8jzKTR|{&NI$G;ySDu+2xfa#J<3%0I%6H9zZPG`VM+=&D z4*ZL440yswdrP&{+_us^8lw6SZNHRV+C`t;Pw{yhmCfG!UUC|zc8e?ih)g&t6W>z& zfnjR%zTmx|jJ;!vN$RxYH0YfCHL*@gXvBGzD)_jphc0`7$%S=ZEA;S2ft_&?K;?}f z(o1<&I~sZ?xbwH4xxfYIT$5Q#$ZVw{p2{GCpHKxIB1hd=fSaNKK5rAzsE^{BaYRu@RDs(crA{->RjpIa zVt78qCso5rrjoR$q(@TB)oVw&bghjRSBh@OKRB0QH5l9`y?NrsV{#@#ZMTE2p(qE> zjFJES2rnPDqtoflN_#>ln>>7ZWs+9?B0CRR`K4)^?FUV?b#{$PMHL!4OQ3c1a*yDk`3`S03H%B*gr@B|Y$mlK%0QLbB85c+As(+>?C zsB?YLWC^N0a{5!a0$aHKTg(Jg%!O0*Vbt~_jy7{Eq2W5NvWc#E0%d{lZ8T3*EBK%B2? zVqE%j7Nd2Ej=niGrZ69#@#FEybaZxoBfWmKF-vg#LW_$fG0;~{F>cvogoL*%EepK( z`3TQ_W@FzmBg2k)iq5;)F8-zy6|?l|4U^I c #712472", +", c #f2dbf2", +"' c #f0d4f0", +") c #7c2d7d", +"! c #ee9def", +"~ c #7e2e7e", +"{ c #d36ed4", +"] c #eb91ec", +"^ c #f0d5f0", +"/ c #9f3c9f", +"( c #752775", +"_ c #cb67cc", +": c #eea5ef", +"< c #e8d1e8", +"[ c #965297", +"} c #a650a7", +"| c #c1c4ac", +"1 c #d56fd5", +"2 c #f3e3f3", +"3 c #fefbfe", +"4 c #7f3080", +"5 c #ecd2ec", +"6 c #d26bd3", +"7 c #7e307f", +"8 c #eebeee", +"9 c #a850a9", +"0 c #7a2b7b", +"a c #e9e5ab", +"b c #953896", +"c c #fa9bfb", +"d c #b5bbad", +"e c #bc70bd", +"f c #fa98fb", +"g c #904e91", +"h c #e698e7", +"i c #e5e2ab", +"j c #faaefb", +"k c #dd98de", +"l c #e4cae4", +"m c #e6cce6", +"n c #7c2e7c", +"o c #ccceac", +"p c #faabfb", +"q c #9ca7ae", +"r c #bb6fbc", +"s c #cf88cf", +"t c #9c3c9d", +"u c #e990ea", +"v c #e3ace3", +"w c #f7f0ab", +"x c #d16ad2", +"y c #fa9ffb", +"z c #ebd1eb", +"A c #acb5ad", +"B c #8a99ae", +"C c #fa99fb", +"D c #f2e0f3", +"E c #98a4ae", +"F c #d16dd2", +" ", +" 3 ", +" ,s= ", +" ,svs2 ", +" 's8j]sD ", +" w&-B s;pffus#", +" }r:cfChg", +" wa&|AE $Fr:y![n", +" *x1ek.t~", +" wiodq l96{7/@)", +" <%_~b0^", +" +94(z ", +" 5>m ", +" ", +" ", +" ", +}; \ No newline at end of file diff --git a/Source/Core/Resources/ScriptPalette.png b/Source/Core/Resources/ScriptPalette.png new file mode 100644 index 0000000000000000000000000000000000000000..20f9d693a12b7d2d6ee181c776d24bcd276ad0dc GIT binary patch literal 932 zcmV;V16%xwP)x#Cdkp8ZFi}x_u6_*b)H0u{9^RK?v6wX6Qo$dUsrWSC5P5J~+~FY6 znM9T?YL;xR*c-pcFl^$kYn5mWzN{GGShd|Fydas|e#LRUnC&4MA^jmXbxA~~4zv2` zeH24U)S8a6V7!7(U505QKv*2%v+BDXRs5N-b8<;rl+1=KjbQp0Ya2bs2pZ0?Y;Is& zbB?OvL}~|;@ayH6>T@tX^zA<6@yk|@{AAv}y3|{IiePRUYuU*xn$=j9=W(ciz%iiY z?Ya&QE1OvUJ&&8JEPC&_2yRFCplEPY*bC5?q%tpd4BV^FR-X9$JJGUO_fP=y_}Eo`Z-hUWYiO) zX?PM*pBfK%>ucqQB`tS$C(PsVaP6`TP1RWnii??;oMzVRVQ}NV4Ky1c(`%c<^1#h4`xN)aXL;)Xmksd`>h@RKt0k5B^ zOb`-K~#90?U-#yQ&AYl&vdRx z10jT<7pNeL{E$=#MF>GJU>MabrB^XuGAhVi5~X0|OKx}9slB*nW)T*csWAtULEqthcm8|D?{FcNDne$d5qB}uTf_j%8Wx5Rkg@s zZHx%OdIHp#U|&MmM9nn7V#b^-xrl4NQ@$mfkbPMF1sf3n^SKWhCIui{UtP%zD&ue$ z&Jhb3A?)LX{pu_L%uxxD-q2}wG5b8}(XJqPMqSH5?m8&n+mL+@O#rumwt?a_0K`g= z7*8jNHOB%*yq*h`V6yOC4M5U|Gpke`M8^*y`%;6lFJ0gBA!8A0tK_{s2(Y6K{1Ar-?3_O+tTY;E>I!^nW=9a*|wC|&fj^d!J* zo;I!&ysr}Q=mn|)m@i=uS6TrgVJ)nv8$6cq8_HLN_XWdylb;A(d8+fO z0Z2|_5NtKVZ?ZJi0L+mAh&v>HG{kd(E>8Q4K#M`KAUow-8UnyLRvf^CPrI?NAgKd@ zkxk>&P)00zT5p?Hx=H|tsfqSW#DxQ}OJU`5MY|Z&Fz^1fE$A20wvjZW|Lv~01_Jp?zq0!m-an|!}K`r z%5obb>jW(a^)Nt20^oK9KuZ2l0DdB3MsZVcR+;d54*|Tb9)O1-z9`GJ;%6cpS8&{z z%y_Z@>0=1Fz`O_mOkvC!s$UJjMluHAtT$Wg(7UXa$YYF(5#hNiF2M8d!7|8Ba=aF; z0NaR*@vF{WYJsV99N>tcZ4QF3>$#ML;hD3N>et45dl`QI-vuXkAB_?I<@J+%t zA#tNc680SmfVB9FRIFLlIouTR`|SH8@ld`|69CUm!4u_B+Elp`e#n@dr(&)VzHY+q zCG6RR{TE?h#h5cxf3^%ToQY1*VKE*bNqP%+A!rD(Jc9mJnFpvDV&H$jaL4%r XA=h_h#im*400000NkvXXu0mjfXgA@s literal 0 HcmV?d00001 diff --git a/Source/Core/Resources/Splash3_small.png b/Source/Core/Resources/Splash3_small.png new file mode 100644 index 0000000000000000000000000000000000000000..e9cc67807dcd5a7ee517ec5db91e10e8860e0837 GIT binary patch literal 28183 zcmV*PKw!U#P)pF33NqRbVF}#ZDnqB004<9jRpV!AOJ~3K~#90>|6&_71!1tI^0XO zTzcj&y?0P!Lk*%PCV4M@ zZ_ixd+ZGox-(}sbM~CGzrDXR1%~+pA}%ieN6i1$A|)xsh>3~)f4d

    lP2+xvSf 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 z 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

    CTRL+OBrowse for a script file to open.
    CTRL+SSave the current script to file. This does not work for internal script lumps, save the map to save the internal script lumps.
    CTRL+SpaceAutocompletes the current word. If there is more than one possibility, it will pop up a list of keywords to choose from.
    CTRL+FOpens the Find and Replace dialog window.
    F3Finds the next occurence of the most recent search with the Find and Replace dialog window.
    F2If supported for the type of script you are editing, this opens a website with information about the current keyword that your cursor is at.
    a80Y-m4@5t7#GqDt|n8yL|9E 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

    GU$f-s;lK}s@~m-7etyl?V^ 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%0DNfVF; zuIrx*sP(HW-~RIGfd?x^y`ROQLD^f#%e8CYNp-a>od_&+<5~D=PJ@b+RjSYc&CEXL zKk;ojYRd84f4{RLgF}0D1J26&dPS};RHS=gM1$5&29lJrWwJatLMG6r>ru73E~Whk zc^J99^cxQyw50cSDi}jC7|yu%+Cb(!NRc{1xmYlWY5irL3a>TV3$R;uppRX~lyY6f z>p=%ViMX;>k>%S8FRDg;r4d~6XHe_t7OtK&=WAR9agV={k_cHxF^rPFe6jbPne6TH-EA*7&4@sNgN<=00HL8 z<%=>lF)5{y!_1Z<@D|@=V`_FI*~NPjD`I9onCCftrp5lEz@KUU*PE0S?J4x|-a+>8 z~pxo8>=L;P5t9JpxmH=0FJ>>ub7x;9j~kN3aR0F`6?M&laSR4pyI$j| znL1!GnT-@GeeUq%!^2r(siP5a7ZqF_KC(x9rEyF)3Q<7FqGl8_2ytd zXR|r5wXFDTOTeDy^e~I(0Go80EjdmWhdM|y;gSeP=ly!0F;6m1Tmk6}&Y^F<_1ko{UiCfYNy+waq!mi^*7A3#_{jU7j}sc$8j?CR@541;FGeqK=Xv#HM> z$alDJeZ3~$-{_xP2fdwCzx{*BI9@gBIAKts)+w}Ft9JTP8A%nn`U-8^bu1s1r*w0v z*~tnJ$26lWqM;)N38-mWj5n+SDtZPn>-)!4!}mr`lErV7@i!_wX|&P8HPCp6#i0|& zg0)eu3yq9ysO<6`rV&MiBk&}I)6|6K}CijDk; z0E#&SL=o>f?kAkLB>1s-u2}Cq=eh-K8Ec{&aS=z)BolMZr!-#()sE~PnJi=3pif8E z-Sx0)Yq9WSU-X-{r>A8CCqx5ivEHZdbEZj4@1Q3qbpw7(6Rka}3y3`Rct-|%yE3DN z^mN6P1Ue7*B})3$F+2KSJm#yVVL#THH1ni{?mfwVyDLwW%_$8!@%jFq#5xwg-?=TV z`Xq^6VOaKiGSU)a1BR>Kn55qDgPmPzw_9v;L2vJR_kc)sz0qV+!CrR#&1)x`{2ZW< zxyCY(h-i5n%u1NY=0K$oa|$m`Tw-t}FN!%0fS^o0=OJzH2SiMBJDo#J6q9*GOog-T zxCc6l9K+9ymMY=pMJ*LN^~6H z;F{>XN@;~0Xt_@EabZSVBO@#!X?sEw^$lt1y`w0jGij1?{qWvXGg1m13pec00&D)| z1$iVLSzo{Fv9{#uK4ofxqtM*m-eH1_6DlTBx#^w1c;;F8)q9rtD9e4 zn@TnQ9O_d4Q}26Ezh5V)oV$2Y&b_rD4|QEFUA;zAkA1i|Y9jZSj!jMA_L@^y|K`$l z={?w%*UrAgRtEA|3UemSX`U3Q025RG)KY*}vmw9ydQa|b+?V$0SLMtrZxX3yzEzlU z3jmF(8@Z4it@9C<)pK+6+_QkX!ihFOo1Pl=VsR=BB>N(!2#X0^dPI)f~$-)@c$6UGjR}h=^=&iFrDJ z

    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­X%b|kGqOn}^W~~se&GVyG*Gd?y=dt< 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%0DU$JysU2Y^+BbZwJ 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 @lyY?b@RYW8NPShTwRBBDi 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

      KjCfB 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 CQzl#h literal 0 HcmV?d00001 diff --git a/Source/Core/Resources/Status0.png b/Source/Core/Resources/Status0.png new file mode 100644 index 0000000000000000000000000000000000000000..d6ce7847d661071f10210dd4af44356a298d4131 GIT binary patch literal 922 zcmV;L17-Y)P)@6zK~}z^fJ+4kaKDzvcPwdiz+C2T5oKBg14jobv1%34jD<{;VkwR^6u?GXVSQvqp;?I~S)qv79nKipBms#{d(rhmpU8iXI)zK4I@R;4D*waa94BogajA6&k%LENQ53BGxB?tw52JurB9F~SA*aMq0dn^SNSX&=Y|&8cQ!8kZhA_|*urhaRxS;3>^e+iCL-W*07hsABd09GX||XMF&HXIQdy#N z$mM|uels57-Z_Z&hC#-yKr9!7_1s#-_?9EgJ0FwS@fh`DBmzr^BG-`R6d&(3iNd&t zo~)B#{@a;|=n6#~mP5v_!g^l}k~kSQ@+%R=MIgi*g5XyZFr{=X@@teoC{&YDYq5TT zIv9(dhhSNcQ2ceZKqC>UTr$$w6v+8lMD;{tNq02nwN8h$WC7GRtzSfO6uK&%bcb;Z zGoyAY;sw6)?sTMfr6Pk($4)jA3BClxvuTL;q+q5t2>ToMGG}!q{xOPX=ru)Kts?SV zB)_6VhBN#{6n9lX2j z%c!#=AH?gTD8M*a4ncBPczgxL)FvJVvZJV^5s?Z;&PSK~ImO53>G)@(D wlDZ`2(2-BbO6nTl__#7s&GM42@$Mt736tb{CV%Cz!wGF|g*iEcTh8eXQ; zY&C@}^knL0>1ukmb~~Ma?^za>(TCr8;T+!Id7uCLJmi(5ll)C5rO9R^^_B)=GMh=Y z!9Xz*&Nvkk_Kg1Iizm9VtE-tQ(q3jypNMxVk{SMFavXnIk>IQ>%Vn!M10&0(3gpcl zout-StV~Z`FPzKVil;R<(Cw(h8@mx)xeAIj2Fi>LLZg*aPL@QEY;9~Nj~^B&GY&10 z%JTPN%)`Uy`wE}ng3 z%#M(^4dHPcLSO83bztCiJ}#xr#?|wCgjQQC6BU@uPf3v`n^h_1!Tew$0wWLL|Dc29 zsiIUO?5#lflMcd=4nF=CxSUx~or*;hTsI zQSxeP*+C51VOeEH4kcxPDzp<@jq11S_L z8p?n;dJN*HqlkP=gwP#}?q@Th&7On%dFi~-$Px0WvWk@4J;+zxnTes67zCZG5bW6m z@xxAt1AD0?0inKl1mCSgq%Q`2EelYho{L9C3SRzsv`O^rS!czKg>bd5#JBfxKyN%E z{kstwNT8xbNWHrt_HRM3e>M7E#GvHbV$|qW6D80fYO9Ob!VAmL^L#Z8Y&*UW?m=WQ zh1Q-%vp5WC@BqTD9SFF$!Ct)__j1?5R5uZ#d=+{f%jk=b3Uwt*A^MXL@TMZ*I)cb> zIu&FfwLKMvEmCCS9gr zZLIyXl_w8|{Gq|hF^-Nh)?v?ccE8Q%d%85dqv?**(WYZ-YV}ix>0cu%hZ7xPZi4@e Z^A9VZr$)*66Y>B6002ovPDHLkV1ij@rw9N5 literal 0 HcmV?d00001 diff --git a/Source/Core/Resources/Status10.png b/Source/Core/Resources/Status10.png new file mode 100644 index 0000000000000000000000000000000000000000..092d0987e3232592f57962a792f2a6acd4ca9ca4 GIT binary patch literal 871 zcmV-t1DO1YP)6>#BRa;?@6dsq2mZKY#c0a}KMzNwUI z%c2&40RKR(7rg5Q-|oFsYCm3lcAh=Y&Ndi18H^qIg{Pwj$_-`-0 z43m*ZckG3&>V$5;4pojS)H-WH3~&^gF- zXF|+~Av!IBs9A*Lw{0;lIWLm_iL=r4mUL_y*n})s7IH4-Bm>Y2KC|2-M>dlUiCY5k z88%6C2B<05uM%d+z1b0O(Lp67eGmA#=-+-klCjM-lD> zN0UwDMltbt!qPR|9?x#ihLTYt-_1I@RD2IO^5{JNUP&n-ZIYsIyf=P(Vk|jEP7J&# z-afS(9mYg)Pcif!JxX1r&^uY{WG$tK)}uwi(E>CvdlOTOfmI>>k|cNWY-PdTGS(8W zb=08E*@jN4lgH|?vPv|e=YcK$^wr#YDX(1&?!osWXt^I5_g)QOj}E@L8N2-QYHTQc xE&4Dr<$XN+k7fFEJ-NEL<|J1*@RR?q^ADPG^OT8{rBDC>002ovPDHLkV1f&vf_4A^ literal 0 HcmV?d00001 diff --git a/Source/Core/Resources/Status11.png b/Source/Core/Resources/Status11.png new file mode 100644 index 0000000000000000000000000000000000000000..2b033bc0aa39115d7b89bad2acbb0751da8ba0e0 GIT binary patch literal 841 zcmV-P1GfB$P)i??xk3JBgYs!*h0Hq^$n4x4nVy-U*of7( zAM#`S+b-X?nQ&`sNo;v;<~wBtuBM8M?#7BVcXwNfYkc$}KW&{63ugNb1o8R?vD=2V zH>*-SxspWGUe83EtPriu`H)szh9o@>tqp0OMW|9LX9|q1bQD2lQJ6m_F%N#@Iz6S4+eVc z-8P$*keA~VY~!ug(jj zDTGi_#IAs)5yvodu`ntlsKE&|R|li6JO;CGy+ssSF*V7Hhx%(hHKj+Hut#x{B8U@M zEsV-oc5aZzpg21iqm~ZO2tT;nM&Tm-%G&B{t$`EmwA97JtVyQrOrdQ{rksaCnany; zFO5QPSHaHw!k$6;HIl-?=P$Eb%aaOEw<-%dO*IVK28K>u1x;r{` z*?ymYpnjU)`e9G!j$v9E{qVVSxO2YWd+xdSQhT32QGdg^y+zsA)~J=$RqFNP;)zh- zzkNM~2*2!nP>v6CeBY%Sm+p*0=qynQH`I-;8OsQd0_BMYySsbT(z6LedvURNF*gTV z%gUgqx*EExs-UT~6e`nX(5;Y*n>MTAm;qUJ-RY#}N5>3Jr&HVw+1X%Hser4f05igs zmrL=WTn-l!6QLzr=5|=;4aW?e4u?`Nmy4DD{$MOB0$QN}1_kDlk^@MLa|1a!pz`s7 z>oqmv&i0P99cmeyG(LQbuL}-@R$pIW^7DZ%ECh~FPyl8E#wJ2m7BqQ!LVZju@MB}V zWUy+tQgsBxjkI-PJ>!PKDM)fgBE`ru%oQ9MM%L`Vz} z4r4Yl5*VzmA5GMlH+z%)wS~9i6TCJq33@TX936ciz$24LN&*h|PUe}YD7X?71Z`=j zVEOsaCX!)cdRjT!+9tM;(sBO`3K2w4&zs510~3Btxw$|icagBM*oanrGM{3`$cI5zB7<^JpxzxD8*YyHt}VZ(Aq*c!R-+J80AFWDCU fd`$m(s{g|eecku4C7Lqi00000NkvXXu0mjfot|W8 literal 0 HcmV?d00001 diff --git a/Source/Core/Resources/Status2.png b/Source/Core/Resources/Status2.png new file mode 100644 index 0000000000000000000000000000000000000000..386e0d5ef598810b9e1fc680a51578c3fd2d6699 GIT binary patch literal 875 zcmV-x1C;!UP)?;jCV z8u$RwiV#Gkq8BA5jxxcrmu8kSo3EaRWz*Hn3aK1sni*worMc|R*VN6?9%w~H%U3?; z>JRfI*Im&4+*2mM6_zP zvx;zj{xmIj;;Zf#f~vv-QWCezDvpSvvNuLk1qn&khO(<9pCFhO(APIW=xeH_S>fSy ze#{n_%1ZFsXuwdL4z04w5Jzl;G%AvQuG2_o0Nhodn$3h-u8gYg@T*{nA+QkTL%wW74znxKfnjJ+-$JdO2L#AgDWipQ!WEr zQUvx&5$H4_Iz8s2%*z|{Gx^pjfIG;OgjB8PT#N)R0v)yi%#lr!69FNHi9*~j*E@3y= zxM(=GgkqF0QnPdkbj7ltBRr_8CgsakV^|OhXUcZCM0>zwrhz?i0L+O);0`3ixqk=j z`?sQJ<#JR z7R7bza9ss8qO7DWH*Z)i&1zDwYx(n-{&lMVqwltnbxq9ZRp|f#002ovPDHLkV1lk= Bklz3R literal 0 HcmV?d00001 diff --git a/Source/Core/Resources/Test.png b/Source/Core/Resources/Test.png new file mode 100644 index 0000000000000000000000000000000000000000..a1a46b07f1369a07cc254e150dbb30c23b1383d9 GIT binary patch literal 450 zcmV;z0X_bSP)pF33NqRbVF}#ZDnqB004<9jRpV!0bfZ(K~#90-IBeE!ax*-Cu+p_ zi(()_2rN-7Oe_L!L<@f~jg5^Wl2$9pJ7{lXpFUOI!FTY1j&}yKD66cyM{gE0sztpU=M!C>D#%^E}LEGfbyb z-*sKq>2w5`&yT4RKpe-|?REk*9*_NYyUmhB>2z9*BuTFU*XtFR%LS*?37gFZlgR}A ze&06?gH@|lRxXzXGXfNr;oX0!Qaq^JSKG~W7|ykUBGv|24t sQ307hp0@o;6p^>2us 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 literal 0 HcmV?d00001 diff --git a/Source/Core/Resources/Thing2D_1.png b/Source/Core/Resources/Thing2D_1.png new file mode 100644 index 0000000000000000000000000000000000000000..bc15905297e05613e234d16652687da6468636ab GIT binary patch literal 11522 zcmXxKc|4Tg`#*l}nK8C8MwZAnMJSaedtxL)3Q-7A2-&HqFt=ohEJ=&VNTii1vL!Q? zA`zvKbx^1*BimqR?(e)l@89o_dwHD4<387QJa3P z0f6GY0$2dUyZ4=vfq4(XFe}$c0E9RFcR^o=>F|(fa^Qcu>cT( z^+7YI==_-nf?>yxS0y=PQ#7vs*0Cz|vE6PDW252I(ecR=iK0RQ?c-YgRd@bnRZ%?t z1UjkQbd~XO!bm1d5eR~66*#R9X_c)y#7FSeHVGdA>$%H;^RXZH73Ae_U5)7V^!hXM zJSbxoSvkoY#uE*8i00h1d3f(Ue@OH08tXd9BJ9C=$uhd{*U&J*xWu$#VWx0?a%7lP z+||9sJz+ven*_gxv&1s8xqQjiiD3fxCedOvRdJ>9R6m-Jxql|fgDQTJe(|ebztEbp zUpd?pF>wlyxzcb}>)wsu_Yyea(w`gMJDO3uTD+rCb~lf9+a(rUeO>=`=xyX2?(KJE zHNR--??-3v87fT}=+6yPw*V~{`HF>0`N@M!oYZN-%bHd^hxbPvPE8A;)YiWAA9*vS z;pbTvx8sA+2KmtI?_pZmGaSc+w)888FVG~pPFCzk7C&=K?SNObG+^$Lnvk!*PV;v7 zu^i}(XY0-PHEizDXmLG_7-#=dNo$TSGdO1JYYxkyukMt|caJb-cz0T#4x`1Xk7=l<`rTwvAI8oqry zTM}Q3BN)#x$pz#hgWVttydK7Ues{(vu*b}ZaSe<67hR$meZxJ zj1TYR&H|#s+tUhb-@kbY*na{y(e`4nsTepq9{x-_V`zVNROR3oR_YJLEqjNh&HF9x z-)onMRtqSzB2fXkYya7Hk^`azc+UGC0w(rmNpz9XnD^fQKjR={A3)@SZQNX}*;XpmzZ z5FV`|kb0Ef6|)=u1zrdkv;p`Em9akZa6`PQ8rgEp@TDWV=T&_1I&|DF`-Y&T5K2HB zhY`qDCOnZ&2>~@@VL75;IV}B6BuxmsB0ibNwE2Qh5Z(b#%>>N-bN^;eRg|8pS@Arw z^KQ7d=B=EEYK{)(>DssLN)DeRnmsH-C7cuzaKg0_s1TH4xvRz%e&nD4&J*WXtZ?i+W}K(HSptA6glIh)J()IS{-U0m&-lC>ta zb;G1ip?WPMk@yd61|cpIV`zp<`-k14q7+Up=dd#HU*+Sb(wN^tl@u`oe&+(e6$FD} zf79i=u6ZNJi#5whb!X1>!dD6Se^5yT_ca}~?u1lcfg&=PMuUzm6y=xDj zoJb!Vf2amXA1SD$&%7Qxemg>I<2fMsp}!r4)zAm=)I%t`RENf}(LfD{wl(nr-~F3U z6MdSi-(T!kh*aDA2bQ;-zU0?kr_xqbV^`|bI(DdEmCiZ=T1YgqDE~ugPLCpieln?p9t{wDF|A7P^ zlfLLM6?OK?+4Z9=wTmD2^h+u#T_~X#^{C7Les?jOleh4Ll51+NKmS}EGx8k$)Z`ry z7KW0yoq$L$9L>X&{bat6RM&xXJZtPdK5KLR=qqP5j@hk0-S_Tm;>ch6>}d96-08T}vENzGesgB! zmPWRE{Z56=#U9<1%}8|MSsG70=~*`Q=7ZEEzQiq?!v_w^6wYF(+N94=)oBxcdUZ{B zc$~ZchK8z4W$MWKaNFEFrr=Bhr)IsoYya=#XAwD*)xC@Qk8?a-Xc^A8J&#l$aT!tD zHBb0Pkxfan0aR&REmQHRNTbLjkg^+@DX(q_V!3T`OjeQseKAH*Vl=58!hqfhBX^pn zhF%UQ(i%If(J`9)l^x=Kfq%E{({ zao-7>aum1`Bz$>NrRN|>2UMZHbbJV+VZF7St8( z79^U!TU*{+6XgBq>qMay#1n*;t*2|n=_lj)a?n8!w|SxY={D@T^PPJeXB9Vr-5>i6 zbJ$U1D}Ip(={5Io@n%7E2Y3JUe!W%wRu!kkv|Q8lO>q~tcEy{U+?EDK%d4t2TSdmC zed<=CR1QFe=B&XuW0T3g&m+&ulAVak?tMSO-Qxc;@TVL}0+ItY;~(zh<7a92B$kA9 zl)bAT{q4@pCZ9$8J}zmDsTbd^;am}yQYgX2cXHC7OgeW`eC7+dDG)rg;aMcpl4i^w z*1RHshcAzXu1mH?rN(xI4y{;jp91G(=qJ#4HA5adlhDM?k#;Z2Hn(xptjx+t+MUP` zpZTLX$JhDUQIx$E_l0m^plumZ3Ol(s%mdygQ%S zmw~&($@r<;Cm1H}yo6TJKsAT~`u4uN)r)?}S}|~Dm<2UK-FLEMv=AiyY}^lStn!B6 zY4afBch1L^{LwTNV88d1F6sU>_pnAx8tka4s9!-mayQIn`zcNQ@PLr1UP-*IH&4Kh^>dfR@=kkk*H_M3_46t7W!km1kV<&tld?;NqSamgU-Pt@Z3 zvndrcF30HS`r7LHq{4qB})(8c<6++f!ti;%N2l=V{*l419zFX{_ z9jh~f!DR1VeH64NzAgv9L1hL;z#>kVnB9;U^qE$v{ArL&xyg2qSH{CCJWYb4ZwSGV z&LXX87E%6%exOr9d%_N`y<*pbhF=1wL>VaQb;o{%pePoaWW48P3a3+>tciOve`yMs zj#+Q#?@!HpBH-l#u<#no@Uv%~!KqNh&N{9kZFxJ;QGFCuwAMhWT=OHCSo|RQ1s$H( z`P#uAA2Nhhx8x3akf1+EMk_PI|_%;;Xk0ltFU9IU5h+I;p%%7G= zk-P^lY<6s(hOR1#>TmMg^4PMD6U+zF4jL%2hWYeh7l?G{Ro#fLdmNQDWk zjvsw0(Dr_N4GfrCaVd(w$OXl3Xre&j8z_|QTpjiw@tfkhE{`T^U!AO9a~6JXk-!N? zlU|(vTV79SHbmz#Q1mJ$mOk;JfcbYUbFTi8D=V^{s||iy7S<^;H0N4}d0gy#3xv0W zj@?{V_;+rkb)7mdIbT2D!%uJS-?HyaR2*RKXjrs6inz|38fy1(15;$gd!M;A)@P1-jjF6$JjwN6tvh*%9c=iOtMl-OU&%xWLZ~7_Hj6ZUJCQlf)lYrAG^z&# zHuMvel9HAx^Pqmo%b-K2fcvS_6d8=0sie{5NIIw=V_qjPK@1Prf>n!wqZT;1uQ`6V zbes#5cqo_P7=p+2pK|Vg-i#C}bE;)PxvbpRI4i23ONc*YpQm!f@hkc5?7RqgBc_%L zAE~(ysQ1ubdp>_~HTc_~KhVUT&pr>VdO)E})h{spael3*Ae8&G^)2F%%^k#m)5u9` zBO}YEuw1@5LhTq)KEjJ=o1pPK^=GqGKTds2DO&P!lX({Q_>9~QDZo*y(BI0b9_4an z<4cRTdGSw#0TC&Ebo|knh|oydOcJa z97JnMaWPRi(Q}8L6mY4@b=?ncP((M!NSS}4qxB03Qes<9l8QXck3odw!#6sRq798w zon=Blv?fkAQeP(L zbRv3y)Ni_-SsQ3thkaL6CTcHUkWkK$sBUPT3VqQKA$gG=?H}A#=md%#dxOHbqWRo? zz6i;JF4iEQasfIqe$1T?G536?KV^N+jq1tel-|lggue>_-55YuSpDuWz;#p|m?XPt zgU3?#a=p|$19?Jj3YqjRHJFm@F9{I*TSYG3jLH8uxdc^MeiK!w z4J8bQDU~((*4U%y=VSO)?{-Y@OY1OASN4ck76dPZ3sHsQ@(1FD1Gev}Qbw+^6lEEpWl&zUNV~PtHwhX%K&=QX5d#Mp z@TrdFT-tl)akEqQ`S*@%=(qI{U4j2={3uJ^@g(BMA-4tx7VN@10AsMZS)o6ORsBQ_ z&6Ss;&$o}Z8-V`6WQI)O-mNm=`vSbMHcIArx`M)46qN&SgsFz2wLy#xxMuV{bjPNR5GA1{*vzJ^B3o{!^a8e{Z3W<0x|cC zZaua7o^BZZxqTN2OVUdkLj8RDZIOm>;2HaFXee=X%*Z_>O&QphtRcJe!-8#AobDaS zF1wJ50blsCpe*xq&x61j5xSZya@RK?MG`hJa4CD3ew~$ImcAohpAhZomf9 z2BHi8ZDjGm^;}Px+4*@d>JWCQvkyAKIK#F@(GTXs6YVM{rhF>5Sdv!b!0J0xC_`2O zSNi?#JO(K{H{1#!DxOYGeg&RF}WB0DT)q07(Aj)FJH3Q$T3@~JL#X){6E+U!#0hYF?wbh#<%z29McPRa9g18SSig*U z5=^=zUkIwlt1c*!k8R6W#ve?~)r-8hGcUr-0OQipa` zCR0A`uMuaqrtFtxq1?w-bkRV;g@4Bh+%Z6>E5N*8^q#6!K6vGN{7eG~+8UOCf^!XW zfu_`&OK5zd*VP{={{lrKYJeUhRM1IT@3S93$@%`@{5V@-)-SyxNbDS9gwfCu z0RmBDS|vctBZ!WXO9&lrD_Sr#0x5DNEZB!1%AOfLP=~g6 zx(Ighldv-{yP5c=Sg{m%qgY905&~lamD=P&6yTT#qyAwGAx7ma8Y1n&wC{uTc7l6m zK-!LvZYAfST}8vd(I`?j9xNMxlLXZL!`0w=-G2ChR3aP4*j!k7p!uyZ`8xeQJ>x68 zOKP?K{3Jd~#y*y#azh`v0RuYKJ`?&91$LHl(6g9sv=<)o+LZAK#SoF3lhL(Bkx<81 zdBvs&PoH31kj-SM86P|isQZCKA4J80rpf?nRTFw>OL*Q@@zdKvkz8IOG6PSZ6+I5T zj_RBTH&9+iM}GoovmseSKa{PPGYklGl*)a}8pHN;l8Jqi1ELeC_35aUw^?eCO- z0Z6CS0Lv2{Ho*MFPxn6s>NCuRB!QCypx1$bn7v`<_c?h4Y*DlbeG^ELhpod#D$Tgq z%!3^n^yET&nd%=haB|_fa{}O6&1SDcZ_-g&6L+xdgPj0y+zxP!eatuBMnDte&y!5T z$UV(&;>l`gud9R>^TpFb=N zFPh{MFz3Ywj#}&mE!qrhoOBArkjKMgC{X%=E`5=2+z{o0C)NR7)`>#geU9wc`Q=AZ zn_*utjk8b@T~ZW7!L-27NS-L%If?J}#(_WSboEeSO&uUL*2+>i+PJ_{!8iAXYeHT+ zzwW4^3sapiRJ}qMfevL*EH2dYh~y5W1z(}4-AnOY%u}&=*qKqLeRdI|z8`A5qlLY; z2imq71gUH86Q&rg zj#9W~vJwhtxCy~Ak^FA$(u8M%(*BBtJxQW`U|Ex}4_|*3Xo~uUgB!}AJ{6%i@`H}xmirC9Q2#*z;LJ&?e+m}qLBP%rtog^OAbwu{blSB!{iA&O5idSqE0?saTgM6 z(2b&QmjhexKuY4^U%1yrQ*hBd>sJlIbQ`d3dd0%xAFd%tSlCz|Q{`=$)+W@qZKQkw zTUA}6#KGYn;FP;%8>gIq5Y0aAJZ2EXzxd?nsb5pWu}tTC=AH}QVa@ZjSbl8oKKH{# zrtZ#`L2+1SNo~TrPuZ}&Y&;K~@g(A?w@F#EyF08nZkwOt&DsL^NsU(0AIk!9hj^Wf zej9iQ>tjfY-3*d=U5&1|0>~c6Kjs)M_w4YeOX3`y#rYcVhf>BC#Qx{MAAcGUTFu+% zC0wMG@Ax6m1U$aKum8*k*xoH(Ndye|VH9uA3D!v69gAUu(uqNXEy)vsPN4p3!r}<5 z-+v+f3s0$q=>zbxAQU7*?Bs*v34C-8mHs!gM)3KVg@4h;Pajir*OwdRzD)@ISWVH! zK?jT+{xj^+9^Hv0?eGwHoY3=zRa;IIW5uceG|=bf1=T`?*5%6@%bAh+&8tvG(`nYu zPDEpZF(jsW)#Hb>2qw9d-Bz{E-O-HJu9=~$%a8)p^3b2PQUq0^14N$RC2q?aJ_TOm} z+oC78)-o*DKG<3nrJT5>q9X06hpi{|M+vy0q|fwHWpNm)Yw!)IqQy%u;gvV;Y#KC3 z>aWi?nHs4+@$RH%;^?El1vK#d*dFH>>zuBL`771CJATm{ar8hb2HDZRK|O4HL8JDr zrr{b_cVSk#3+5-5cw5BldM6mK+(OSs)gNKeUgC&N457k2ky%+yxsmfRXIVm|%uxq( z`vBvqhf^B3%V_ONEw80yxFkz@SbsyE?&vQPX`a>B4D!497zt)9A7aBMuf%>nP1hUX z5P{pUvh*UT?GDYY0^6Mj>%WJ#hW02nY$C~!l*L42cR7PQNj2Ax`$)nH2zCvNN!#Bb zMd0V$u;`wy0>PU415u795AhibM>=*hu#D}(nyO$$7t{)<^?}KW^IDLV zAN$Ly*H3;Kr*atzX&B`rLJYFEGtm{iMyE>D@XsJ=THjCwYv>uNmTR3p${YHK)g6i7h^cFPlkeSXMOE2XF(lb z+ZNIszYjV(t>GA!V2vwecWBfZ1wCCK$I`tz38bO;h5|iMc$A}g@CvpYkQ4WOiF?!; z1TrsIDG2IfqUCW!7KS0M(Bi>cZh;owjt@AN2o)<>&l|QQWs;Xgx;+L(SyI0c)17Dm z+1HTOLpvu~2_rmrrYr7?6v@)g<*=r4V)^?J)ck!?{Y9ntKfEhq zP$1Gz+`kV;|I~Ul(L~&fq6OWH7w^CV$H<# zRG{IJHHkAgY{h=q`hpTnv$1%Zt_PM7qq}s zBxL9R7F-f1%tH(;X_D;eHo(wf-ekv3t!#lsg=24VXP@GT2d8UUB5l|G%TKk60578C zOY8E++<04)-?y|Uy|9MPwKI(g9-2Z+Ml?s&?NA74bUX0c8upaO@9 zp4IR5AKwOn^zQzR@b``}sb4WO7f7+&KXYe}S8Du^r za5={t-VG9&G~v}vd4ClMLz&kEr#nf{?b9@2FSqz1Jut;4f8=#bc9RxcKv-oi@;7O_ zHrL$BtUF7J7%DN4z~zd3$FzeY$ya9ppe34xeZJ^qRcub*pc)MD$Fo{@6Il9m4Z&4? z>wAg<;tV6weYj;b1dP0bEeF}7rifz8g8>jGb@cTvupY*G{1fxR^tyxu{+-aYN->Uj zUu++*cSAQdZ&*R3Yayqe*2T>7%ik`ED|5dI>)`x@0+frTr;;t@y=5cyvXkz4n{Nq8Nbn!*+%Eq@}_W z)T>?dNUk0t!v|T4 zteiS~#o{K-bE4eMT=Y7r{Idy|-6GBEGmq`d+ZXD%H8%O1*Hg4Yz;v=6{P_)RNt+wp zg{+|pwRiRQB6m~OLGWM1Ajt2Svpiy^TrGCqx$NLBS-7+ak7JkLl66O>CoA%tY(}+h zNxjfxLF0edAf5FKNW{9vC$a*vAP=h25=bm5qId@fdEw_7>obmQ=5^WX)SVB=z=y_n zx-iQV>J*l@FXtQ%^;co0Iq+MFXR+P}gQKiIw|3;w03ZFI0$h)n4re2dHVreU$4#?- z>Rp{{FG=Z?fv1Y3Ua)Q{ep{xku($a3{B6R7xc!08#=py;UBMDkXfxb3uBkES?k`hM zWQxj8ZkRIPa9cp~|2Km@mT~1A5t>Y~Lt7AWrrLW!JQUU5g}j!Y;$Ur+npOm&;*!-8`U=sSY+b z^ShwHU6p5+zgNG8xEfzCsqwT$oR6)Usur?W_se{>>q0gmT~;AV(`D{LzMvpwC$m-Y z+1qbFzZ27f;UjP!REA!XM{)JKYimboEI;-y=DdW0*f3e4tWoaO`n)sOJ7R>fr}rPS zmzDE>(cHE-CuO$yGFvIBSD!_d>9{}4Ke^i$R)^ln_>*GGy{1Ttf?VT`sh6|}R<&&P z7C$gh#+_P5l8U2dh7mue;KyLh@NM;nIJ#1}Ac{%rXm1#blhJVWjJ4Pcg{|H3S`buz@?!4PK-bu%uY9i|z4&u4WxAsT zsDk~$L?g>?fAi66qaB>(GsB+a$gMk^>IqA&4T^daSw@c?JL~CdG=Y%+&%Hf~{}JGl zz*WT1U~o35(KW}YG?#R{M5g;lVWTG0gGHqiE4Lc#?-Aqb zw2}v_xc`=DjI@x?tX0+8^uo6hJ941tZJ({tVRVYqmsQ)&0ONE&YLh0kagcrv^;qQK z8BF7a(IoEvg?5qg0nGl@zSJYM&C?zal-X%i`%hdWZf?~NnyBNshG|(j9zXY7yAMSx zUxV7%im+*Ze2?NC(_M#lRPo}q`E-!31F6bVx0r<;7Kr((Zi`%v@#9BWzH>A+EZNuPDt}nD3(M`{GB=*Jaq<>6>4T>X zEc_qg9H6Ryrhv?AE~@5}JicOQR0^xnXLl2y$1^MmHc{y>LK7*%$n&E4sUfmu4mUYW zW3FX)4gT&#kr!(sOK^6tWVZNgMX)tK83 z+^-wrLrc0}>Wja1z(d1iz_phm2Ms%XyI|W--_eQroYv@d`&EETb$p(llIASkwbxIJ zRb5$6?iA(roSYj~wLwH`w%i>%ke}|@@{ke`$5Jhql~7dpPe2YzeakL8NB-IOY54Nq zSF)z@6U?q8Q@Mmkf`{}JET0}T-WEjaMU}o^r(1YeWQCynM3icFz;`YZ+^cajp)TFX z4;Yc-6gxU4$UPpSB}b4IZgbVc1V6Xt8pyPc`_mFlo5&M|*R1}-=Rk{Dy}_{l2+M~1 zyy(`1GDe2`gQ+TSZBSo!Ekfh@wev3hq+HM4UITEP;j7B4mqljm$NfAuGu*zE7h6ue zz>;@JIgHZMA`nZ5^~fnxdDU@gng{vhid_osKdv2gCp?}0X#jn$AZr6zuD#Iqh2)4- zHRN?F)kPW_go6Y-#tJLrcZTnN7{E7n;dX(mI=x(K+Ox>n$gbS==xuDX=Pn^+_S+jlV3r#we!JxkrT`ek$Z4$z7(eu*dD@_#3|6Zn?wOMk(*Mj>@BD*WA z-?=;LPETMy(Q!wK(FBnrmqZqBq+uh&we}PanTn{||28%9V(PO`vo7#(X$MYi2H4X~ z({rtqN~HOnZlX)!Q^Kl%dcVd=^jop%4Q%_H*%rwnYpRXOMeL>K@LD=oIo3J1ZT$w< z750_|SXe`pMBTzs8l?^G0 znUGNn|JN69Uc6N-In%TAs(F&6-1mzFxmI!fc`Ijhy< zD(=b8cP#E@M~15$eb_*Wq#^fd<@;-iO9i|D3bcU94m)Ju$Vy7;(R3vO#4Xy;Ur?R> zUA1qqbyD8{du9{v#R;lso_O2Mhwl5Ssfy|$Wh(ytwmQL_EY~*oSSZEOy|IN&;toP$W4t)xO(p0d{ma~p*TJ)E{(X7} z`a<|$J9s%#(-hSSH5C`XM9mb=cW!2cN=%m*)zc(4J9Hr;zTBq&L;KU}#-ASf0fZZQ>Wu-UC_)oX~U`uAu)~xE2L5+KQG^@Vutxj?7 zSEhO%ddEHIzO{R*-c)poffL~d?PSYGkej=Mrt)Gfl6W+fd?mY`pvBcj+?dJoRQZ3S zA3qH)`6z`DUy62PO>+_72i+&z7flKOa2%VaIK$f;pPmeQ9-Qtftv3}e9rM|_A;Tjg zCu(D@KzWL98Q;p8SzVhK;3<|MKW-Ef|*+Ufn%G-`u2Qjc@j&m>7vneBH@3^T%20e_vThnYn|e}n-YQu~ zAeq*I{Qn`Nq664uflSkmBv(!Gvvi$%LXK9qbdOxUt{JaWpo4EndZn3^BjTE6wngO8 lwhug%TmQ#aZ+*6WGyJz^{NIH|-uY!4;;%^I|6s`YBT)cgr8YmX7ZZtVo5pAdpJ==Au&|&r(Ew0&vxNWdsoN1pd%rfGW}ALu5}7mnn!C7nPi9PpBhhc?a$06qPC*5#alv@ z9H`nt)ZC)qh$Pn?wKmRptjp1!+o8>`0{@0jcq+BN(~$8|ed4KV(J>h}$TYrV(ZQUn zi0b;aeQIz|N7z;D>Nq>zI_mSis9>_xBR6llW`Qe*XIpb6?}z6Bvt^4rD{474Js>wkjAjR=gWs#+eVY{(8civy1#%F03A~e9%EJ5{ zTD)3ruQuLR;CJHhdBc_4ZoY)(9usvRqVPr7`4?}(z zkV+^qply4sFniE zzEsn%&(iAQR*&?EcG+c;`7p9OKkOvUYhOy?isa`$`z-olt8_>e4D%BRdbyKj7wh{k1rW&*S>`~2swpeZRY>!f zOo#0WlzsMBQr#j-VzO0w`JNxf8Je1cCS{&WYU*wOCKCIBMIH5w=8$5*Ie+nhGxImj zBT7C-%Bn-?{=-zHD48sUw)7uP3K$aW+1XWdeYmgzFxdu%KU_$370Ru{WO$SD>W;~! zShsITQ->wBlU!k8c#e!9iyf1D%)pcz*M4Cv3jJz8XJ5{p*{-Hr@hFMJN$OySq9-J2 zMr{NZw-ndjMn3q;e~X#?O^<{ybd_|O$A>^X#DVf9`lA2*n|H^(3Ws^U=R5B=V5+e4 z#*F^({elEpj2u|@NUFx+>lBR@JCg^Nu;^;V%4X~9kl1nt9td-MqeV}4VoN+OY(rc- zp~maI6j%C3r1v=`&I>l%Cy=KE6I2Q~U?H-EP;F}dR3WoZ)TM@P-JwS@Jk7Wkma7Ys zsF%+*qjw@_+3jX57=d`MPzC8~Z)f|QjbR}_w;9-BC_WEj`!Mf!Q-o(c^faxq>nE~4 zroLuXUE!4SxX7UkuX=uatSbeoBNsBInG3wso&NBaa%Dz~?oq_cfFm0UQ>`FIxE@=N z2dR(y^>WZC4k^^Gk+UqNh&bW8K_$fOf%UdbyLI#)04;C(wQ= z!M6o^C!QJCqdc>z^U4REMFu$$_99cQd*2V%zjvTto%X{H^5zZn*f>3C$iFaWq77Pq zR*AB<^3>L&luk5mwB3Wx(s#wA7lM7S^t+mKZhQB0->)5d zha1;`t=#hM3@m(8M%lPMV$tG!L)t&!d*a`m6QZ_j$23kFPCnsXZY3aMrp-Hg{V#)2 z{-&aP;+wUc5qI&a8_~$0R+&8WSFp?42#<(U761G;ZpdqqP}zIBV`gu_UblDKTD##s zq+dt*=UxY8&Zi&Env|M{kyQ__?CQ!4y>snLQISIb9QoP~r72PSrW)cN%VI~-Hj5_2 z)wec@{;Tkn?1_Lu7r;MrNRm;&Sh@aw!o`@=ef#1&XZ4c&0xxGfB#k5Eg+reo{VIE{ zp7oZs5WC&_Rv0WA*)3h)xeeCDKcNoO8N4!&HJ6(VOKrn|?b?*!xcFD-coaeQ=C)y> zw*IpG*cW4eQgAa~9laHWP>WlqMTlgNyW)W`EB8e`qi*=Xj;&_uv6j5uVa?sfCv_4F zCWKnLF{2p~G3jB&0#-t@wztEZnseowb;D74GbEo%qn^x?N-TalXU*BYBONH}x2-3h z57q)Pu(9T}G2olG9=gjXJ9KsB^`bXO3s~{v_B-UDj#9(d?W|IFk1Znx?A1z>9pbOD zqJneBZ5V5$#S}XzIfL}w@)%F`!0}BzOjeKRi$|_?QgPe z3Cw2COpdSetWC&s|1a_+Y1$BmY!Y{EZye~|2Wq_Wa*TqJAU^29~QW&lQ)qckdsPF*zblQ`C?7@~5hRV3EQ}bK$LkWt%m}SpNEy$fU#c zf?>~ug{?>vqGw&$4m{L`f_@@${!(XBizbz^uBKamBOJc;2G8_P3UVv z(Q;LYvWty>G%+P1YVcn_m=JgK8AU12E{m}AKSiWGS-?TMv)j5I4$??+zhzG9*1kDN z^J-{XcT}JFR*9q;@}EKqG$~{YvdD07(!)hmlYG>U$4g!xG@zt9T0!D0skle*+HhafWwQd5@ka(Fp&2p>cMsL1qGf&bqSA3gJap?RFkUwR- z76EANUQz!{BQ%Q`35$1KgUkA&BXPkT!x&+`s-Tkdgzxccee2Ne<&K}!plN$5WacZR z-&k=ZU+$I=Cnl0(IYF*{3NODUB3gch#8;hn9M+fFroAb|YIRs&s@#uSQR3<`bC)*d zoSur5z)lwYyM%6i$jZfUj@r()jIv`|OwcGE?5I8>7=V~Ry+!-M83XMyi)Lcj5M~tx7ip6g<)UL4}6OgO2dMyzuvx^j$nbBm3)$(Sx1(P!#GWzmAK*ceAAR zza`lM>yRqH6%PoW)W@|C(a#O&dc3?%Mx3-t9ia~F3qt-egh4?WT3?bOkXs*}hISIZ ztHD24_WX!B>klQ(KVAV*&3Ad44@`$b2OjgEv$aaUKFNcTMOs#k2D*?_ugUepADig@l_dp2g9^QNAK4*+oUvtFPu zC)-2-KnmitHBJ0&7PGluI-6Oy^)=LF^QN2+H-cN<2kTTk&ENlpC>RVBe7W#TK-lI3 z@tQldAUuj)x9ra=VV8Iq8<{!vVzIma?$T(jk8NmU zNN-rBOVGGaZ|%-1EvYPMF=U|DXW6E1hf&J77PRQ$?*790SI_Y9wckHFlegAeVgv#L z?%YxR=A33dP~x1am+G9Igf7Ctcbb7<(?Df5V~K%p-we5T`Ke~6rhiI1UcH@)eeuTH z!1rZ=?EG8p)?~^%ry6q=4t^~C5u-?VP%PZ&CmcXtS|881ZTJ4srnN7gY7guSK z2vCk)qZ0qFP@(P_%SYJ>Y9tgmE8BC@{rV1aXLJNW_TxQ(8{KVfZEol2)9vxOMothD z`H?s7-;S8jjNELbMBsy*>*)lx`RlF*Y6LuB0mcJ=V>i_^mF+*AM^d@?+`;uy4<-nA;!ua?z6}jN*b{ZC?-rfkGxD^Sll%4D@c#tm@)W5L12aB+IPZSz z7FviMa3p{RM@PHY7lwTD8*W#$tY=VEWbou61bo5KNYaIH8wpebB_LFPw9mo%Frql7 zEsh*-p@j_p-;mP`Xkg$jGz3QeT%c>{3_|ULf$V@+bxZ0LI&~Kf^exS~A=+bjp#c#F zFc92nFkT+Qals9Hygza+Zt8tMJF)tZfxwy?$_-KhLKS7ecW`T&nN6EvXTf^j5;_+M zp3l}4B+uyt#_7tq&;+m?D~VHvkRbGC9y06#f85J?#omC0{sd$tl4~*@-9~K{CS+>% zX~@w&?-b95{pd(M%Z+6xbOYfM_wuUyUNzDK8>+PelUm?_54Z!2k|#_<1!0^)>Cc(IQS(e`2gC3 zyvjl6PwA$9Kigi*3xNxO&>%6Czd+x<+!K;XM*FcM7phy)_SsB}(x zVyIt1U(TuJTTAU;5K%xB=*JU5KD+V}os!RV$?=4Hu~FpAhAAsu%a&DzyY9)pnBhw9 zQ8?!URe_UAWzxz+dPQ2J*hcY@#cTdWlM^796I1WEb~DllTS|sk(>Oc%L~1aH-F^yz;^}_TRx_pUpkcgV4I~et zxW43wAx^iML{9z9@_OhdCd~sPg*$amI>_Mz$NT++TL^(}D~2lIgw%&6BDbs?jki_i z&&WE8bn3C^3Gvcj%k!+mI`_x)6A6IgY<)d!A@L6Ssao=xsf2$G!{gBK~MjD$*ZMLt= zQVd2~8SQT!&$*!k?OZ*^+{Vhe^)F- z2|^CS{TwXoiv+eXo6)1|KXfRs{642|J3{Q@O3u8W%A_(R)1m&>uI~6nrg*e{$rjQl zF`~nD!W(S=IAM*0_PV+cJTl*$Z7*y$XeJ+T@=lxDQ3DrMGd0oi*9DQPT)Fx}Yq88U z+=TS$&8%yCp#@s=Xf7phSNbG#4%c5&KQf_GRP>Mi*^NfysaDMN$~{WWn+I)QSelQw zCZcyf@fq|bwtW|s5$BA%1B%UHb5x9H&FH9h!Xp@2d>+4a3noFmcKTEL$Gt8fZhYCt zm$w+H!QR0fbQs&FhTDypV;+w9gG3ulL~c?JjI4c>=4HOxW7k^kRy|Co*z#`_>mn zzHP;v9@Xo6j9k-xzu)<)`Z)j3WQvQf*Jf-1W8+NW#}5(-bbiurjB-Zesy{nt1ha|XRFDw+x9^twG(OxnJ-p3PL~@EF@jASDpm=es1|wOo`P@Tpk7YU(0KYZtck36b zGwx@&<59vx^7_g{L=&T~(58Onx@dKCRsXZU0iM(#*_kL43Un_8dQ=L${?Dwz>xg{z zw(NXc!_WAAE^}*VG0QN|~ZI{~u4~ZxC*dkbV1}?tFBmToxZ5cAdB!=ilV~Mytan3^z+#Rf z{JQb#S+)2doAwzEboSWe zo|IQLes{-)*F!~#qxt*^exUou_d5+r%~sVhVfA(o!N(62+RA=n$s-#D=vo_~(z{k1 z#B-o_$h;AK-X`d6$B+8d zZLOEpW>0uDxaNPnv>#mXXb(4ITT7DU5O8dBygGbc$S|?mxl!%nUiRK;GzPitr7q)b z1?7cH$logw^H=7(n3}pFW3*8p@eK*xpg6F~8TeS*QE#E0^Ld;5=J`ltlrEXLX3N5O{a`g1pFyK6?Il$) zrZaju0R`PK=rY30(Hb=scY-4ov6f129z34LsE-)2;vAO#TyWkn!4LP6G4Ws;QNMFn z|E0i++`JE^z=#yTWzjsoObLXs?WR!=b83bmPLwS*;&K!GrNWh(T}ckH zB6GC04^QQ1f?TW)$VAyu+@*%)J&XS@913(v`$&{2=H8qgQpoTd4ms=)qLuV=RRZ^(dwk8cvZR_of{tByYKc$*Cm>IDE=q&A31T_j| z%v&;;3qB(MhF&9v45zZ6BVPH%PAUnY=h_qR@cQc6B2L6spgd2xA_Q1&2Qq zi-=~S!d4c6)u!Y={wc%1D}X^66KUge=vr@+saaUF!X4xExQ_A`WutEHNm)&_@J%sC z`)j3)R9^iDh}rV)DfQMTp*TfV*2F45oM8lTi=Di>sTE^EcMg+@18&w=*>XuYoeeMZ z7V1HMT63+O#modPu8GW6=UN!-A@-?;VbpFRHFwJ^KH6rD2DMDqa(}omQES#+xvnA+kWf$Ep zm;vas=OLH}lDNZ1j!?u)o_CSil?--4^M)jjT{U1@i{Ko}_8WUsyh~Cpm>zlG-nkL6 z$NcloUQZm!mw_YAps?cQy6NW2TrZB%AGH{RMtGX}Um9q-JpJO%5MrezeaLtKz3?16 zIdO#cuEmNw>gqdA2kzFs2N8qkSJ$4Re9q={WJw3$ObYQYXPL5*`9b02th%f8FR+kFhS2#mi!ukiPG=stU&2%fr>b@?#q10RP+BQEh>aQ_84YSIolIQ0#>rD@AU28P+QtRaCO3@9} z>##E4PI%x8Cw!^!X$W?5(%{Oa8W%sR`mEUUx<%vmxk-qK$Q9l~>TV@kR<_=yVBz=h z3B(ek7BnZoaS5$Ze!1GEfEp~+b$L`Gd=oM9H1Dlykv^$!L$`LbyYU-ap1ufZ3)qeT`Pkfnn@IzbdRL`6_-NMjI@(@~hff%VXu zpB?ZOT-01~9r?Yx;hv?jawuFQ0(0&6LUA*=4e6-JbxYFH($d}8B6Z@^>}(x|b{=kF zYwLX_EiaipbyJKTdO*cqD&2?fCN%7N=gylqm6a5AzXkJu&m^C&Sy^>=wZ}_%XP>$y zRkrWO#==aNcW)Qx8In(7I}WL>n44f)n4F{nsGd*57MlcJplOAusJxjO4_fwgmN&d@ z=BuvB;!EP52aGGzILx6B-@)SGaU9hfa z_So$%%Z7uyi&o%xK|S14uo*z_Cn)HO$_)Z1#F*gD?6kf+_VuH9zz=?K`++8D zFpj#(iB!M?u~>m749~9HP&a#TK-Ixmzk(Wx48j~uC<60e*X+CFLm$OMH@}Yp+BaPd z$nj*uNuuND9lip9cl&kW^|ua~;Yc+u0r;Yj=QlP{Le$c!YGqGxzBK zvGb}cRn_Ig1TqvN!cwHU2}tnGm6J@eJm;~PJ{=KAr5DunRp@QCn^*SlyqWgNE@eqs zqA0+1#&4mcS7XQHcDTi{&F<%qUnQ~RDWSBqbbtZ6vXLR~ugnHx{%Zi>bS`PXI&T;8 EKNYKiumAu6 literal 0 HcmV?d00001 diff --git a/Source/Core/Resources/ThingBox.png b/Source/Core/Resources/ThingBox.png new file mode 100644 index 0000000000000000000000000000000000000000..132dc5e0645d6b848bde3794276b995174f621b5 GIT binary patch literal 482 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 literal 0 HcmV?d00001 diff --git a/Source/Core/Resources/ThingTrigger.png b/Source/Core/Resources/ThingTrigger.png new file mode 100644 index 0000000000000000000000000000000000000000..62ebd2daf256bcf29bba24c2bc1d886d90368742 GIT binary patch literal 705 zcmeAS@N?(olHy`uVBq!ia0vp^_CV~;!3HEZ=*=$%QY`6?zK#qG>ra@ocD)4hB}-f* zN`mv#O3D+9QW+dm@{>{(JaZG%Q-e|yQz{EjrrIztFa>+MIEGZ*O8WEvzx`nkRtCl+ zM+}-69T<8u&NH)I==f%X$rRwW)%p4PIlou!sY8bjSxDIM3pn^}`J}@mEG)sy%D~hy z@m8-)kd4^|O$LRA8EmH>H!HF-un1gssH`zyVQ^r$xOa=ZPOB^jQ0%U4zwR=2t#6D> z9i3rvOh*k=H}uUB zXgy5?T9WasGV8dAE)ze{zKoX^Mb;)?yi^$&T|TGyXK^u9&WU4{cVIBQsGKPj9sBT; z#o8byAoo(z3#s|;p1qM#X^agFNlUKour-~exAwCg(1kkZgI^U?iBtiD!=qnPx`@lR zN~D5;vE|XvgGPH?)*llChRTbJ(G3hz7w55aED(HKiHn)EaCIt+l>V2zbOvB>_5sx; zNp?saH$B0)_~le42Br*` zIbpB;`TN`a?z9Sil>^r%vv4RJn^+UY d<pF33NqRbVF}#ZDnqB004<9jRpV!0VzpDK~#90t&=fsgD@0^xkhe* zDK4c7H!9rVy7L6KY`gAzTHaKSz qElUdXREN(Re%9tJ(yw`j)#B_*XDja}S&=8lUi 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 Hu0mjfQDH%{ literal 0 HcmV?d00001 diff --git a/Source/Core/Resources/WarningLarge.png b/Source/Core/Resources/WarningLarge.png new file mode 100644 index 0000000000000000000000000000000000000000..5a5eea40048ce3a505290f7985a46df101f9a9b7 GIT binary patch literal 749 zcmVsu*!ys&LpkOSkbP>mLA8Xf%2t$UrJh+P6mbSU&b+LIivhVfX_LSR;Y=LX=*tET+U_ae_jb zmY#f!F{IMztd7lLHE9N&GWobxq0-Qy``8Rii!FiYjZG~f$%=|=S9;7DfUg9Rkx{}B z={!MsSuPwF7E1w+TSvCFb-Y9%5m~GR&h|sVKm-gKx#|^CDBKke4+D(F!PBb(EV}_S zX?do8HN^q@+bPVS1w3b9sw00GArX%kk*H7M#?4!ID5WYzt4yX1>QvUC)n&0Xlc`Tp zGmKoBN=v0u`KB%Ncs#16=23&oH8d}iDH6Aomp~A{!))%h0tNtKy zK4#uGIR!ANEH8vkUkHbdB_>Z%U48%hKg9}#S{4j){$(*hqqJBDnOp;BH(8J$3BbZ# zG&Q%@F1NPuZb_0 zRb7Rbtm_03kL@R;AO?;YoYY0(aMDqD-yJ04IG~FdbrH_0LZ7u`WREB&f~u>lZ|=8u za$kC1%#IGW&Q6ZBbDoJ$oX>6N0=_{m;2-3E%?-G{(>Ax;VRku(jIPnouUy0S*RF2BR0kYI^x zM2T~LZfHij;4qIQ;MIU8FXK2*>|=8F>!}|u6Lz?PGsbP0l+XkKc&=MI literal 0 HcmV?d00001 diff --git a/Source/Core/Resources/White.png b/Source/Core/Resources/White.png new file mode 100644 index 0000000000000000000000000000000000000000..0f4ea34881bd891ab663d98dc564746fb06ba71d GIT binary patch literal 140 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz&H|6fVg?3oArNM~bhqvgNU+2; zqQp5rH#aq}gn^+`s~4m})6>N2BR0kYI^x zM2T~LZf|~#&jUQ(_u-xOQ;!yp2?^lB4wF2?Xt?U|RtWwid_I#eD kqI02bm+ApaSmdKI;Vst00+uGYXATM literal 0 HcmV?d00001 diff --git a/Source/Core/Resources/Zoom_arrowup.png b/Source/Core/Resources/Zoom_arrowup.png new file mode 100644 index 0000000000000000000000000000000000000000..7cc806b470d9a7f38dafa4eb760ead1d8003c5b8 GIT binary patch literal 207 zcmeAS@N?(olHy`uVBq!ia0vp^l0YoL!3HEBKfAITNO2Z;L>4nJa0`Jj ; + +// Texture sampler settings +sampler2D texture1samp = sampler_state +{ + Texture = ; + MagFilter = filtersettings; + MinFilter = filtersettings; + MipFilter = filtersettings; + AddressU = Wrap; + AddressV = Wrap; + MipMapLodBias = 0.0f; +}; + +// Texture sampler settings +sampler2D texture1linear = sampler_state +{ + Texture = ; + MagFilter = Linear; + MinFilter = Linear; + MipFilter = Linear; + AddressU = Wrap; + AddressV = Wrap; + MipMapLodBias = 0.0f; +}; + +// Transformation +PixelData vs_transform(VertexData vd) +{ + PixelData pd = (PixelData)0; + pd.pos = mul(float4(vd.pos, 1.0f), transformsettings); + pd.color = vd.color; + pd.uv = vd.uv; + return pd; +} + +// This blends the max of 2 pixels +float4 addcolor(float4 c1, float4 c2) +{ + return float4(max(c1.r, c2.r), + max(c1.g, c2.g), + max(c1.b, c2.b), + saturate(c1.a + c2.a * 0.5f)); +} + +// Pixel shader for antialiased drawing +float4 ps_fsaa(PixelData pd) : COLOR +{ + // Take this pixel's color + float4 c = tex2D(texture1samp, pd.uv); + + // If this pixel is not drawn on... + if (c.a < 0.1f) + { + // Mix the colors of nearby pixels + float4 n = (float4)0; + n = addcolor(n, tex2D(texture1samp, float2(pd.uv.x + rendersettings.x, pd.uv.y))); + n = addcolor(n, tex2D(texture1samp, float2(pd.uv.x - rendersettings.x, pd.uv.y))); + n = addcolor(n, tex2D(texture1samp, float2(pd.uv.x, pd.uv.y + rendersettings.y))); + n = addcolor(n, tex2D(texture1samp, float2(pd.uv.x, pd.uv.y - rendersettings.y))); + + // If any pixels nearby where found, return a blend, otherwise return nothing + //if(n.a > 0.1f) return float4(n.rgb, n.a * settings.z); else return (float4)0; + return float4(n.rgb, n.a * rendersettings.z * rendersettings.w); + } + else return float4(c.rgb, c.a * rendersettings.w) * pd.color; +} + +// Pixel shader for normal drawing +float4 ps_normal(PixelData pd) : COLOR +{ + // Take this pixel's color + float4 c = tex2D(texture1samp, pd.uv); + return float4(c.rgb, c.a * rendersettings.w) * pd.color; +} + +// Pixel shader for text +float4 ps_text(PixelData pd) : COLOR +{ + // Take this pixel's color + float4 c = tex2D(texture1linear, pd.uv); + return float4(c.rgb, c.a * rendersettings.w) * pd.color; +} + +// Technique for shader model 2.0 +technique SM20 +{ + pass p0 + { + VertexShader = compile vs_2_0 vs_transform(); + PixelShader = compile ps_2_0 ps_fsaa(); + } + + pass p1 + { + VertexShader = compile vs_2_0 vs_transform(); + PixelShader = compile ps_2_0 ps_normal(); + } + + pass p2 + { + VertexShader = compile vs_2_0 vs_transform(); + PixelShader = compile ps_2_0 ps_text(); + } +} diff --git a/Source/Core/Resources/mergegeometry.png b/Source/Core/Resources/mergegeometry.png new file mode 100644 index 0000000000000000000000000000000000000000..c9c44274a6af66d6da812f16db72b3ea3d2d82b9 GIT binary patch literal 530 zcmV+t0`2{YP)$p2m(J)I!eTOdQuHQ9=g~o0UIPi6wG)H zEKU4~_nHHXr;Q3A1!B61a#|H}>%7*YyADw?OU>g<6MJQ>4`siP_afNRO@0ixDvS9! z$uIrDw;__G70KO=yKa! znZ<@-SU#VRClmmSVp7#I;b7x#h+XNT%x!aJ5xZ#zq?6otCX?xyO6FV==b@Z=4a|CG z9tLR%*6qSla!G8hi;A?j;F`>>?pXEv58AwbrGwLpruJ<{$qh?7!62Yf&lovXCMMiT zn8k0OL>pJnv{JpLcDsG48~9};DKY7ZNkxv6=E^KyI=i9y{rWEhC6^+mne??x%G@?r z7I6#f1FykfCR10Lt4ig8tTo&ay(Uq~#FpvkMx=@JAmKG|GYQIcO#1&f=f61e1;JyK2x?K7e=w+pi)d5srG-nE5$^g7N)go}@FPSYK*B}b z&YkX~H^U_SzMiEgMBtZd`J@8r@#6S=zFG|D}dRCW!JoK@50g506B7O!1 zL;Qs2D+d<4jjA96ro9GbE$*B(I&|M7IxJCpcc+cL7S>0yKg4qbWKEMl1Fp(q{_U6O zb9;+y+vXfjKz;H!H8D&w`Z#U_RA%w(`Wp4JHbtY+C9EgF9;g{6Rmmi19-+@|b7dA= zmX!|#0yEO;yqMIDOgPw>4zVv?w76}qY=|`p=Qui(-|3{4%=uWHM{?%4&&)$0C&B72 z3Q8_X(deQk?agWNY+^OveE6c3n|IngIBy&8mX+MFloJdB8V{^7{-25ocM@Xp$2ZaP z^$W%8chv26JEnn>kz~YVASN|APL340d47?_; ztIR@|n6JNt=p`bV6rEAIjmQvJK+?}Z2OE ; + +// Texture sampler settings +sampler2D texture1samp = sampler_state +{ + Texture = ; + MagFilter = Linear; + MinFilter = Linear; + MipFilter = Linear; + AddressU = Wrap; + AddressV = Wrap; + MipMapLodBias = -0.9f; +}; + +// Transformation +PixelData vs_transform(VertexData vd) +{ + PixelData pd = (PixelData)0; + pd.pos = mul(float4(vd.pos, 1.0f), transformsettings); + pd.color = vd.color; + pd.uv = vd.uv; + return pd; +} + +// Pixel shader for colored circle +float4 ps_circle(PixelData pd) : COLOR +{ + // Texture pixel color + float4 c = tex2D(texture1samp, pd.uv); + + // Use shinyness? + if (pd.uv.x < 0.4f) + { + float4 s = tex2D(texture1samp, pd.uv + float2(0.25f, 0.0f)); + c = float4(lerp(c.rgb * pd.color.rgb, s.rgb, s.a), c.a); + } + + c.a = c.a * pd.color.a * rendersettings.w; + return c; +} + +// Technique for shader model 2.0 +technique SM20 +{ + pass p0 + { + VertexShader = compile vs_2_0 vs_transform(); + PixelShader = compile ps_2_0 ps_circle(); + } +} diff --git a/Source/Core/Resources/treeview.png b/Source/Core/Resources/treeview.png new file mode 100644 index 0000000000000000000000000000000000000000..0bbd274f6727d1abcbb7926a8fb597dc0d7f7fc4 GIT binary patch literal 231 zcmeAS@N?(olHy`uVBq!ia0vp@K+MO%1|+}KPrC%9I14-?iy0WWg+Q3`(%rg0Ai)yX zh!W@g+}zZ>5(b7&tzM9Z7Ec$)kcv6UA3lBh)!*}W?V11E&)huKz;7HO_4;plaoI2a zMUFQjqB}RJ2{2xA@lb42+uz`@V_8ejq5pxLnFVj;1zHjnW^izDwf;VO`SRoi#Rpk5 z7$&fCT8gh$t2xJ-xMtRbxvyWn;V9SQNxs^0Zu#8L4t`2EE4Ostxo{$4N3lbW6Q=}2 Y>RkU; + MagFilter = magfiltersettings; + MinFilter = minfiltersettings; + MipFilter = magfiltersettings; + MipMapLodBias = 0.0f; +}; + +// Vertex shader +PixelData vs_main(VertexData vd) +{ + PixelData pd; + + // Fill pixel data input + pd.pos = mul(float4(vd.pos, 1.0f), worldviewproj); + pd.color = vd.color; + pd.uv = vd.uv; + + // Return result + return pd; +} + +// Normal pixel shader +float4 ps_main(PixelData pd) : COLOR +{ + float4 tcolor = tex2D(texturesamp, pd.uv); + + // Blend texture color, vertex color and modulation color + return tcolor * pd.color * modulatecolor; +} + +// Full-bright pixel shader +float4 ps_fullbright(PixelData pd) : COLOR +{ + float4 tcolor = tex2D(texturesamp, pd.uv); + + // Blend texture color and modulation color + return tcolor * modulatecolor; +} + +// Normal pixel shader with highlight +float4 ps_main_highlight(PixelData pd) : COLOR +{ + float4 tcolor = tex2D(texturesamp, pd.uv); + + // Blend texture color, vertex color and modulation color + float4 ncolor = tcolor * pd.color * modulatecolor; + float4 hcolor = float4(highlightcolor.rgb, ncolor.a); + + //return lerp(ncolor, hcolor, highlightcolor.a); + return hcolor * highlightcolor.a + (ncolor - 0.4f * highlightcolor.a); +} + +// Full-bright pixel shader with highlight +float4 ps_fullbright_highlight(PixelData pd) : COLOR +{ + float4 tcolor = tex2D(texturesamp, pd.uv); + + // Blend texture color and modulation color + float4 ncolor = tcolor * modulatecolor; + float4 hcolor = float4(highlightcolor.rgb, ncolor.a); + + //return lerp(ncolor, hcolor, highlightcolor.a); + return hcolor * highlightcolor.a + (ncolor - 0.4f * highlightcolor.a); +} + +// Technique for shader model 2.0 +technique SM20 +{ + // Normal + pass p0 + { + VertexShader = compile vs_2_0 vs_main(); + PixelShader = compile ps_2_0 ps_main(); + } + + // Full brightness mode + pass p1 + { + VertexShader = compile vs_2_0 vs_main(); + PixelShader = compile ps_2_0 ps_fullbright(); + } + + // Normal with highlight + pass p2 + { + VertexShader = compile vs_2_0 vs_main(); + PixelShader = compile ps_2_0 ps_main_highlight(); + } + + // Full brightness mode with highlight + pass p3 + { + VertexShader = compile vs_2_0 vs_main(); + PixelShader = compile ps_2_0 ps_fullbright_highlight(); + } +} diff --git a/Source/Core/Types/AngleDegreesFloatHandler.cs b/Source/Core/Types/AngleDegreesFloatHandler.cs new file mode 100644 index 0000000..8e9010f --- /dev/null +++ b/Source/Core/Types/AngleDegreesFloatHandler.cs @@ -0,0 +1,114 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Data; +using System.IO; +using System.Diagnostics; +using CodeImp.DoomBuilder.Config; +using System.Windows.Forms; +using CodeImp.DoomBuilder.Windows; + +#endregion + +namespace CodeImp.DoomBuilder.Types +{ + [TypeHandler(UniversalType.AngleDegreesFloat, "Degrees (Decimal)", true)] + internal class AngleDegreesFloatHandler : TypeHandler + { + #region ================== Constants + + #endregion + + #region ================== Variables + + private float value; + + #endregion + + #region ================== Properties + + public override bool IsBrowseable { get { return true; } } + + #endregion + + #region ================== Constructor + + #endregion + + #region ================== Methods + + public override void Browse(IWin32Window parent) + { + int oldvalue = (int)Math.Round(value); + int newvalue = AngleForm.ShowDialog(parent, oldvalue); + if (newvalue != oldvalue) value = (float)newvalue; + } + + public override void SetValue(object value) + { + float result; + + // Null? + if (value == null) + { + this.value = 0.0f; + } + // Compatible type? + else if ((value is int) || (value is float) || (value is bool)) + { + // Set directly + this.value = Convert.ToSingle(value); + } + else + { + // Try parsing as string + if (float.TryParse(value.ToString(), NumberStyles.Float, CultureInfo.CurrentCulture, out result)) + { + this.value = result; + } + else + { + this.value = 0.0f; + } + } + } + + public override object GetValue() + { + return this.value; + } + + public override int GetIntValue() + { + return (int)this.value; + } + + public override string GetStringValue() + { + return this.value.ToString(); + } + + #endregion + } +} diff --git a/Source/Core/Types/AngleDegreesHandler.cs b/Source/Core/Types/AngleDegreesHandler.cs new file mode 100644 index 0000000..3c1f039 --- /dev/null +++ b/Source/Core/Types/AngleDegreesHandler.cs @@ -0,0 +1,112 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Data; +using System.IO; +using System.Diagnostics; +using CodeImp.DoomBuilder.Config; +using System.Windows.Forms; +using CodeImp.DoomBuilder.Windows; + +#endregion + +namespace CodeImp.DoomBuilder.Types +{ + [TypeHandler(UniversalType.AngleDegrees, "Degrees (Integer)", true)] + internal class AngleDegreesHandler : TypeHandler + { + #region ================== Constants + + #endregion + + #region ================== Variables + + private int value; + + #endregion + + #region ================== Properties + + public override bool IsBrowseable { get { return true; } } + + #endregion + + #region ================== Constructor + + #endregion + + #region ================== Methods + + public override void Browse(IWin32Window parent) + { + value = AngleForm.ShowDialog(parent, value); + } + + public override void SetValue(object value) + { + int result; + + // Null? + if (value == null) + { + this.value = 0; + } + // Compatible type? + else if ((value is int) || (value is float) || (value is bool)) + { + // Set directly + this.value = Convert.ToInt32(value); + } + else + { + // Try parsing as string + if (int.TryParse(value.ToString(), NumberStyles.Integer, CultureInfo.CurrentCulture, out result)) + { + this.value = result; + } + else + { + this.value = 0; + } + } + } + + public override object GetValue() + { + return this.value; + } + + public override int GetIntValue() + { + return this.value; + } + + public override string GetStringValue() + { + return this.value.ToString(); + } + + #endregion + } +} diff --git a/Source/Core/Types/AngleRadiansHandler.cs b/Source/Core/Types/AngleRadiansHandler.cs new file mode 100644 index 0000000..0f9beee --- /dev/null +++ b/Source/Core/Types/AngleRadiansHandler.cs @@ -0,0 +1,113 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Data; +using System.IO; +using System.Diagnostics; +using CodeImp.DoomBuilder.Config; +using System.Windows.Forms; +using CodeImp.DoomBuilder.Windows; +using CodeImp.DoomBuilder.Geometry; + +#endregion + +namespace CodeImp.DoomBuilder.Types +{ + [TypeHandler(UniversalType.AngleRadians, "Radians", true)] + internal class AngleRadiansHandler : TypeHandler + { + #region ================== Constants + + #endregion + + #region ================== Variables + + private float value; + + #endregion + + #region ================== Properties + + public override bool IsBrowseable { get { return true; } } + + #endregion + + #region ================== Constructor + + #endregion + + #region ================== Methods + + public override void Browse(IWin32Window parent) + { + value = Angle2D.DoomToReal(AngleForm.ShowDialog(parent, Angle2D.RealToDoom(value))); + } + + public override void SetValue(object value) + { + float result; + + // Null? + if (value == null) + { + this.value = 0.0f; + } + // Compatible type? + else if ((value is int) || (value is float) || (value is bool)) + { + // Set directly + this.value = Convert.ToSingle(value); + } + else + { + // Try parsing as string + if (float.TryParse(value.ToString(), NumberStyles.Float, CultureInfo.CurrentCulture, out result)) + { + this.value = result; + } + else + { + this.value = 0.0f; + } + } + } + + public override object GetValue() + { + return this.value; + } + + public override int GetIntValue() + { + return (int)this.value; + } + + public override string GetStringValue() + { + return this.value.ToString(); + } + + #endregion + } +} diff --git a/Source/Core/Types/BoolHandler.cs b/Source/Core/Types/BoolHandler.cs new file mode 100644 index 0000000..66cac72 --- /dev/null +++ b/Source/Core/Types/BoolHandler.cs @@ -0,0 +1,122 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Data; +using System.IO; +using System.Diagnostics; +using CodeImp.DoomBuilder.Config; + +#endregion + +namespace CodeImp.DoomBuilder.Types +{ + [TypeHandler(UniversalType.Boolean, "Boolean", true)] + internal class BoolHandler : TypeHandler + { + #region ================== Constants + + #endregion + + #region ================== Variables + + private EnumList list; + private bool value; + + #endregion + + #region ================== Properties + + public override bool IsEnumerable { get { return true; } } + public override bool IsLimitedToEnums { get { return true; } } + + #endregion + + #region ================== Constructor + + // When set up for an argument + public BoolHandler() : base() + { + // Make enums + list = new EnumList(); + list.Add(new EnumItem("true", "True")); + list.Add(new EnumItem("false", "False")); + } + + #endregion + + #region ================== Methods + + public override void SetValue(object value) + { + bool result; + + // null? + if (value == null) + { + this.value = false; + } + // Compatible type? + else if ((value is int) || (value is float) || (value is bool)) + { + this.value = Convert.ToBoolean(value); + } + // string? + else if (value is string) + { + // Try parsing as string + if (value.ToString().ToLowerInvariant().StartsWith("t")) + this.value = true; + else + this.value = false; + } + else + { + this.value = false; + } + } + + public override object GetValue() + { + return this.value; + } + + public override int GetIntValue() + { + if (this.value) return 1; else return 0; + } + + public override string GetStringValue() + { + return this.value.ToString(); + } + + // This returns an enum list + public override EnumList GetEnumList() + { + return list; + } + + #endregion + } +} diff --git a/Source/Core/Types/ColorHandler.cs b/Source/Core/Types/ColorHandler.cs new file mode 100644 index 0000000..977e6d2 --- /dev/null +++ b/Source/Core/Types/ColorHandler.cs @@ -0,0 +1,126 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Data; +using System.IO; +using System.Diagnostics; +using CodeImp.DoomBuilder.Config; +using CodeImp.DoomBuilder.Windows; +using System.Windows.Forms; +using System.Drawing; + +#endregion + +namespace CodeImp.DoomBuilder.Types +{ + [TypeHandler(UniversalType.Color, "Color", true)] + internal class ColorHandler : TypeHandler + { + #region ================== Constants + + #endregion + + #region ================== Variables + + private int value; // XXRRGGBB color + + #endregion + + #region ================== Properties + + public override bool IsBrowseable { get { return true; } } + + #endregion + + #region ================== Constructor + + #endregion + + #region ================== Methods + + public override void Browse(IWin32Window parent) + { + ColorDialog dialog = new ColorDialog(); + dialog.AllowFullOpen = true; + dialog.AnyColor = true; + dialog.Color = Color.FromArgb((value >> 16) & 0xFF, (value >> 8) & 0xFF, value & 0xFF); + dialog.FullOpen = true; + if (dialog.ShowDialog(parent) == DialogResult.OK) + { + value = dialog.Color.ToArgb() & 0x00FFFFFF; + } + } + + public override void SetValue(object value) + { + int result; + + // Null? + if (value == null) + { + this.value = 0; + } + // Compatible type? + else if ((value is int) || (value is float) || (value is bool)) + { + // Set directly + this.value = Convert.ToInt32(value); + } + // String? + else if (value is string) + { + // Try parsing as string + if (int.TryParse(value.ToString(), NumberStyles.HexNumber, CultureInfo.CurrentCulture, out result)) + { + this.value = result; + } + else + { + this.value = 0; + } + } + else + { + this.value = 0; + } + } + + public override object GetValue() + { + return this.value; + } + + public override int GetIntValue() + { + return this.value; + } + + public override string GetStringValue() + { + return this.value.ToString("X6"); + } + + #endregion + } +} diff --git a/Source/Core/Types/EnumBitsHandler.cs b/Source/Core/Types/EnumBitsHandler.cs new file mode 100644 index 0000000..b28b30e --- /dev/null +++ b/Source/Core/Types/EnumBitsHandler.cs @@ -0,0 +1,122 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Data; +using System.IO; +using System.Diagnostics; +using CodeImp.DoomBuilder.Config; +using System.Windows.Forms; +using CodeImp.DoomBuilder.Windows; + +#endregion + +namespace CodeImp.DoomBuilder.Types +{ + [TypeHandler(UniversalType.EnumBits, "Options", false)] + internal class EnumBitsHandler : TypeHandler + { + #region ================== Constants + + #endregion + + #region ================== Variables + + private EnumList list; + private int value; + + #endregion + + #region ================== Properties + + public override bool IsBrowseable { get { return true; } } + + #endregion + + #region ================== Constructor + + // When set up for an argument + public override void SetupArgument(TypeHandlerAttribute attr, ArgumentInfo arginfo) + { + base.SetupArgument(attr, arginfo); + + // Keep enum list reference + list = arginfo.Enum; + } + + #endregion + + #region ================== Methods + + public override void Browse(IWin32Window parent) + { + value = BitFlagsForm.ShowDialog(parent, list, value); + } + + public override void SetValue(object value) + { + int result; + + // Null? + if (value == null) + { + this.value = 0; + } + // Compatible type? + else if ((value is int) || (value is float) || (value is bool)) + { + // Set directly + this.value = Convert.ToInt32(value); + } + else + { + // Try parsing as string + if (int.TryParse(value.ToString(), NumberStyles.Integer, CultureInfo.CurrentCulture, out result)) + { + this.value = result; + } + else + { + this.value = 0; + } + } + } + + public override object GetValue() + { + return this.value; + } + + public override int GetIntValue() + { + return this.value; + } + + public override string GetStringValue() + { + return this.value.ToString(); + } + + #endregion + } +} diff --git a/Source/Core/Types/EnumOptionHandler.cs b/Source/Core/Types/EnumOptionHandler.cs new file mode 100644 index 0000000..04c61db --- /dev/null +++ b/Source/Core/Types/EnumOptionHandler.cs @@ -0,0 +1,194 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Data; +using System.IO; +using System.Diagnostics; +using CodeImp.DoomBuilder.Config; + +#endregion + +namespace CodeImp.DoomBuilder.Types +{ + [TypeHandler(UniversalType.EnumOption, "Setting", false)] + internal class EnumOptionHandler : TypeHandler + { + #region ================== Constants + + #endregion + + #region ================== Variables + + private EnumList list; + private EnumItem value; + + #endregion + + #region ================== Properties + + public override bool IsBrowseable { get { return true; } } + public override bool IsEnumerable { get { return true; } } + + #endregion + + #region ================== Constructor + + // When set up for an argument + public override void SetupArgument(TypeHandlerAttribute attr, ArgumentInfo arginfo) + { + base.SetupArgument(attr, arginfo); + + // Keep enum list reference + list = arginfo.Enum; + } + + // When set up for a universal field + public override void SetupField(TypeHandlerAttribute attr, UniversalFieldInfo fieldinfo) + { + base.SetupField(attr, fieldinfo); + + // Keep enum list reference + if (fieldinfo != null) list = fieldinfo.Enum; else list = new EnumList(); + } + + #endregion + + #region ================== Methods + + public override void SetValue(object value) + { + this.value = null; + + // Input null? + if (value == null) + { + this.value = new EnumItem("0", "NULL"); + } + else + { + // Compatible type? + if ((value is int) || (value is float) || (value is bool)) + { + int intvalue = Convert.ToInt32(value); + + // First try to match the value against the enum values + foreach (EnumItem item in list) + { + // Matching value? + if (item.GetIntValue() == intvalue) + { + // Set this value + this.value = item; + } + } + } + + // No match found yet? + if (this.value == null) + { + // First try to match the value against the enum values + foreach (EnumItem item in list) + { + // Matching value? + if (item.Value == value.ToString()) + { + // Set this value + this.value = item; + } + } + } + + // No match found yet? + if (this.value == null) + { + // Try to match against the titles + foreach (EnumItem item in list) + { + // Matching value? + if (item.Title.ToLowerInvariant() == value.ToString().ToLowerInvariant()) + { + // Set this value + this.value = item; + } + } + } + + // Still no match found? + if (this.value == null) + { + // Make a dummy value + this.value = new EnumItem(value.ToString(), value.ToString()); + this.value = new EnumItem(this.value.GetIntValue().ToString(CultureInfo.InvariantCulture), value.ToString()); + } + } + } + + public override object GetValue() + { + return GetIntValue(); + } + + public override int GetIntValue() + { + if (this.value != null) + { + // Parse the value to integer + int result; + if (int.TryParse(this.value.Value, NumberStyles.Integer, + CultureInfo.InvariantCulture, out result)) + { + return result; + } + else + { + return 0; + } + } + else + { + return 0; + } + } + + public override string GetStringValue() + { + if (this.value != null) return this.value.Title; else return "NULL"; + } + + // This returns an enum list + public override EnumList GetEnumList() + { + return list; + } + + // This returns the type to display for fixed fields + // Must be a custom usable type + public override TypeHandlerAttribute GetDisplayType() + { + return General.Types.GetAttribute((int)UniversalType.Integer); + } + + #endregion + } +} diff --git a/Source/Core/Types/EnumStringsHandler.cs b/Source/Core/Types/EnumStringsHandler.cs new file mode 100644 index 0000000..babf981 --- /dev/null +++ b/Source/Core/Types/EnumStringsHandler.cs @@ -0,0 +1,176 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Data; +using System.IO; +using System.Diagnostics; +using CodeImp.DoomBuilder.Config; + +#endregion + +namespace CodeImp.DoomBuilder.Types +{ + [TypeHandler(UniversalType.EnumStrings, "Setting", false)] + internal class EnumStringsHandler : TypeHandler + { + #region ================== Constants + + #endregion + + #region ================== Variables + + private EnumList list; + private EnumItem value; + + #endregion + + #region ================== Properties + + public override bool IsBrowseable { get { return true; } } + public override bool IsEnumerable { get { return true; } } + + #endregion + + #region ================== Constructor + + // When set up for an argument + public override void SetupArgument(TypeHandlerAttribute attr, ArgumentInfo arginfo) + { + base.SetupArgument(attr, arginfo); + + // Keep enum list reference + list = arginfo.Enum; + } + + // When set up for a universal field + public override void SetupField(TypeHandlerAttribute attr, UniversalFieldInfo fieldinfo) + { + base.SetupField(attr, fieldinfo); + + // Keep enum list reference + if (fieldinfo != null) list = fieldinfo.Enum; else list = new EnumList(); + } + + #endregion + + #region ================== Methods + + public override void SetValue(object value) + { + this.value = null; + + // Input null? + if (value == null) + { + this.value = new EnumItem("", ""); + } + else + { + // No match found yet? + if (this.value == null) + { + // First try to match the value against the enum values + foreach (EnumItem item in list) + { + // Matching value? + if (item.Value == value.ToString()) + { + // Set this value + this.value = item; + } + } + } + + // No match found yet? + if (this.value == null) + { + // Try to match against the titles + foreach (EnumItem item in list) + { + // Matching value? + if (item.Title.ToLowerInvariant() == value.ToString().ToLowerInvariant()) + { + // Set this value + this.value = item; + } + } + } + + // Still no match found? + if (this.value == null) + { + // Make a dummy value + this.value = new EnumItem(value.ToString(), value.ToString()); + } + } + } + + public override object GetValue() + { + if (this.value != null) return this.value.Value; else return ""; + } + + public override int GetIntValue() + { + if (this.value != null) + { + // Parse the value to integer + int result; + if (int.TryParse(this.value.Value, NumberStyles.Integer, + CultureInfo.InvariantCulture, out result)) + { + return result; + } + else + { + return 0; + } + } + else + { + return 0; + } + } + + public override string GetStringValue() + { + if (this.value != null) return this.value.Title; else return "NULL"; + } + + // This returns an enum list + public override EnumList GetEnumList() + { + return list; + } + + // This returns the type to display for fixed fields + // Must be a custom usable type + public override TypeHandlerAttribute GetDisplayType() + { + return General.Types.GetAttribute((int)UniversalType.String); + } + + #endregion + } +} diff --git a/Source/Core/Types/FlatHandler.cs b/Source/Core/Types/FlatHandler.cs new file mode 100644 index 0000000..9a9180b --- /dev/null +++ b/Source/Core/Types/FlatHandler.cs @@ -0,0 +1,81 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Data; +using System.IO; +using System.Diagnostics; +using System.Windows.Forms; +using CodeImp.DoomBuilder.Windows; + +#endregion + +namespace CodeImp.DoomBuilder.Types +{ + [TypeHandler(UniversalType.Flat, "Flat", true)] + internal class FlatHandler : TypeHandler + { + #region ================== Constants + + #endregion + + #region ================== Variables + + private string value = ""; + + #endregion + + #region ================== Properties + + public override bool IsBrowseable { get { return true; } } + + #endregion + + #region ================== Methods + + public override void Browse(IWin32Window parent) + { + this.value = FlatBrowserForm.Browse(parent, this.value); + } + + public override void SetValue(object value) + { + if (value != null) + this.value = value.ToString(); + else + this.value = ""; + } + + public override object GetValue() + { + return this.value; + } + + public override string GetStringValue() + { + return this.value; + } + + #endregion + } +} diff --git a/Source/Core/Types/FloatHandler.cs b/Source/Core/Types/FloatHandler.cs new file mode 100644 index 0000000..ef6f2dd --- /dev/null +++ b/Source/Core/Types/FloatHandler.cs @@ -0,0 +1,98 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Data; +using System.IO; +using System.Diagnostics; + +#endregion + +namespace CodeImp.DoomBuilder.Types +{ + [TypeHandler(UniversalType.Float, "Decimal", true)] + internal class FloatHandler : TypeHandler + { + #region ================== Constants + + #endregion + + #region ================== Variables + + private float value; + + #endregion + + #region ================== Properties + + #endregion + + #region ================== Methods + + public override void SetValue(object value) + { + float result; + + // Null? + if (value == null) + { + this.value = 0.0f; + } + // Compatible type? + else if ((value is int) || (value is float) || (value is bool)) + { + // Set directly + this.value = Convert.ToSingle(value); + } + else + { + // Try parsing as string + if (float.TryParse(value.ToString(), NumberStyles.Float, CultureInfo.CurrentCulture, out result)) + { + this.value = result; + } + else + { + this.value = 0.0f; + } + } + } + + public override object GetValue() + { + return this.value; + } + + public override int GetIntValue() + { + return (int)this.value; + } + + public override string GetStringValue() + { + return this.value.ToString(); + } + + #endregion + } +} diff --git a/Source/Core/Types/IntegerHandler.cs b/Source/Core/Types/IntegerHandler.cs new file mode 100644 index 0000000..8281104 --- /dev/null +++ b/Source/Core/Types/IntegerHandler.cs @@ -0,0 +1,103 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Data; +using System.IO; +using System.Diagnostics; + +#endregion + +namespace CodeImp.DoomBuilder.Types +{ + [TypeHandler(UniversalType.Integer, "Integer", true)] + internal class IntegerHandler : TypeHandler + { + #region ================== Constants + + #endregion + + #region ================== Variables + + private int value; + + #endregion + + #region ================== Properties + + #endregion + + #region ================== Methods + + public override void SetValue(object value) + { + int result; + + // Null? + if (value == null) + { + this.value = 0; + } + // Compatible type? + else if ((value is int) || (value is float) || (value is bool)) + { + // Set directly + this.value = Convert.ToInt32(value); + } + else + { + // Try parsing as string + if (int.TryParse(value.ToString(), NumberStyles.Integer, CultureInfo.CurrentCulture, out result)) + { + this.value = result; + } + else + { + this.value = 0; + } + } + + if (forargument) + { + this.value = General.Clamp(this.value, General.Map.FormatInterface.MinArgument, General.Map.FormatInterface.MaxArgument); + } + } + + public override object GetValue() + { + return this.value; + } + + public override int GetIntValue() + { + return this.value; + } + + public override string GetStringValue() + { + return this.value.ToString(); + } + + #endregion + } +} diff --git a/Source/Core/Types/LinedefTagHandler.cs b/Source/Core/Types/LinedefTagHandler.cs new file mode 100644 index 0000000..247461f --- /dev/null +++ b/Source/Core/Types/LinedefTagHandler.cs @@ -0,0 +1,40 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Data; +using System.IO; +using System.Diagnostics; +using CodeImp.DoomBuilder.Config; +using CodeImp.DoomBuilder.Windows; +using System.Windows.Forms; + +#endregion + +namespace CodeImp.DoomBuilder.Types +{ + [TypeHandler(UniversalType.LinedefTag, "Linedef Tag", true)] + internal class LinedefTagHandler : IntegerHandler + { + } +} diff --git a/Source/Core/Types/LinedefTypeHandler.cs b/Source/Core/Types/LinedefTypeHandler.cs new file mode 100644 index 0000000..7cc8860 --- /dev/null +++ b/Source/Core/Types/LinedefTypeHandler.cs @@ -0,0 +1,112 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Data; +using System.IO; +using System.Diagnostics; +using CodeImp.DoomBuilder.Config; +using CodeImp.DoomBuilder.Windows; +using System.Windows.Forms; + +#endregion + +namespace CodeImp.DoomBuilder.Types +{ + [TypeHandler(UniversalType.LinedefType, "Linedef Action", true)] + internal class LinedefTypeHandler : TypeHandler + { + #region ================== Constants + + #endregion + + #region ================== Variables + + private int value; + + #endregion + + #region ================== Properties + + public override bool IsBrowseable { get { return true; } } + + #endregion + + #region ================== Constructor + + #endregion + + #region ================== Methods + + public override void Browse(IWin32Window parent) + { + this.value = ActionBrowserForm.BrowseAction(parent, this.value); + } + + public override void SetValue(object value) + { + int result; + + // Null? + if (value == null) + { + this.value = 0; + } + // Compatible type? + else if ((value is int) || (value is float) || (value is bool)) + { + // Set directly + this.value = Convert.ToInt32(value); + } + else + { + // Try parsing as string + if (int.TryParse(value.ToString(), NumberStyles.Integer, CultureInfo.CurrentCulture, out result)) + { + this.value = result; + } + else + { + this.value = 0; + } + } + } + + public override object GetValue() + { + return this.value; + } + + public override int GetIntValue() + { + return this.value; + } + + public override string GetStringValue() + { + return this.value.ToString(); + } + + #endregion + } +} diff --git a/Source/Core/Types/NullHandler.cs b/Source/Core/Types/NullHandler.cs new file mode 100644 index 0000000..8913ea2 --- /dev/null +++ b/Source/Core/Types/NullHandler.cs @@ -0,0 +1,78 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Data; +using System.IO; +using System.Diagnostics; + +#endregion + +namespace CodeImp.DoomBuilder.Types +{ + internal class NullHandler : TypeHandler + { + #region ================== Constants + + #endregion + + #region ================== Variables + + private object value = (int)0; + + #endregion + + #region ================== Properties + + #endregion + + #region ================== Methods + + public override void SetValue(object value) + { + if (value != null) + this.value = value; + else + this.value = (int)0; + } + + public override object GetValue() + { + return this.value.ToString(); + } + + public override int GetIntValue() + { + int result; + if (int.TryParse(this.value.ToString(), out result)) return result; + else return 0; + } + + public override string GetStringValue() + { + return this.value.ToString(); + } + + #endregion + } +} diff --git a/Source/Core/Types/SectorEffectHandler.cs b/Source/Core/Types/SectorEffectHandler.cs new file mode 100644 index 0000000..9830839 --- /dev/null +++ b/Source/Core/Types/SectorEffectHandler.cs @@ -0,0 +1,112 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Data; +using System.IO; +using System.Diagnostics; +using CodeImp.DoomBuilder.Config; +using CodeImp.DoomBuilder.Windows; +using System.Windows.Forms; + +#endregion + +namespace CodeImp.DoomBuilder.Types +{ + [TypeHandler(UniversalType.SectorEffect, "Sector Effect", true)] + internal class SectorEffectHandler : TypeHandler + { + #region ================== Constants + + #endregion + + #region ================== Variables + + private int value; + + #endregion + + #region ================== Properties + + public override bool IsBrowseable { get { return true; } } + + #endregion + + #region ================== Constructor + + #endregion + + #region ================== Methods + + public override void Browse(IWin32Window parent) + { + this.value = EffectBrowserForm.BrowseEffect(parent, this.value); + } + + public override void SetValue(object value) + { + int result; + + // Null? + if (value == null) + { + this.value = 0; + } + // Compatible type? + else if ((value is int) || (value is float) || (value is bool)) + { + // Set directly + this.value = Convert.ToInt32(value); + } + else + { + // Try parsing as string + if (int.TryParse(value.ToString(), NumberStyles.Integer, CultureInfo.CurrentCulture, out result)) + { + this.value = result; + } + else + { + this.value = 0; + } + } + } + + public override object GetValue() + { + return this.value; + } + + public override int GetIntValue() + { + return this.value; + } + + public override string GetStringValue() + { + return this.value.ToString(); + } + + #endregion + } +} diff --git a/Source/Core/Types/SectorTagHandler.cs b/Source/Core/Types/SectorTagHandler.cs new file mode 100644 index 0000000..57092c4 --- /dev/null +++ b/Source/Core/Types/SectorTagHandler.cs @@ -0,0 +1,40 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Data; +using System.IO; +using System.Diagnostics; +using CodeImp.DoomBuilder.Config; +using CodeImp.DoomBuilder.Windows; +using System.Windows.Forms; + +#endregion + +namespace CodeImp.DoomBuilder.Types +{ + [TypeHandler(UniversalType.SectorTag, "Sector Tag", true)] + internal class SectorTagHandler : IntegerHandler + { + } +} diff --git a/Source/Core/Types/StringHandler.cs b/Source/Core/Types/StringHandler.cs new file mode 100644 index 0000000..7db1ff3 --- /dev/null +++ b/Source/Core/Types/StringHandler.cs @@ -0,0 +1,88 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Data; +using System.IO; +using System.Diagnostics; +using CodeImp.DoomBuilder.Windows; +using System.Windows.Forms; + +#endregion + +namespace CodeImp.DoomBuilder.Types +{ + [TypeHandler(UniversalType.String, "Text", true)] + internal class StringHandler : TypeHandler + { + #region ================== Constants + + #endregion + + #region ================== Variables + + private string value = ""; + + #endregion + + #region ================== Properties + + public override bool IsBrowseable { get { return true; } } + + #endregion + + #region ================== Methods + + public override void Browse(IWin32Window parent) + { + value = TextEditForm.ShowDialog(parent, value); + } + + public override void SetValue(object value) + { + if (value != null) + this.value = value.ToString(); + else + this.value = ""; + } + + public override object GetValue() + { + return this.value; + } + + public override int GetIntValue() + { + int result; + if (int.TryParse(this.value, out result)) return result; + else return 0; + } + + public override string GetStringValue() + { + return this.value; + } + + #endregion + } +} diff --git a/Source/Core/Types/TextureHandler.cs b/Source/Core/Types/TextureHandler.cs new file mode 100644 index 0000000..7f1e6eb --- /dev/null +++ b/Source/Core/Types/TextureHandler.cs @@ -0,0 +1,81 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Data; +using System.IO; +using System.Diagnostics; +using System.Windows.Forms; +using CodeImp.DoomBuilder.Windows; + +#endregion + +namespace CodeImp.DoomBuilder.Types +{ + [TypeHandler(UniversalType.Texture, "Texture", true)] + internal class TextureHandler : TypeHandler + { + #region ================== Constants + + #endregion + + #region ================== Variables + + private string value = ""; + + #endregion + + #region ================== Properties + + public override bool IsBrowseable { get { return true; } } + + #endregion + + #region ================== Methods + + public override void Browse(IWin32Window parent) + { + this.value = TextureBrowserForm.Browse(parent, this.value); + } + + public override void SetValue(object value) + { + if (value != null) + this.value = value.ToString(); + else + this.value = ""; + } + + public override object GetValue() + { + return this.value; + } + + public override string GetStringValue() + { + return this.value; + } + + #endregion + } +} diff --git a/Source/Core/Types/ThingTagHandler.cs b/Source/Core/Types/ThingTagHandler.cs new file mode 100644 index 0000000..f1cf8de --- /dev/null +++ b/Source/Core/Types/ThingTagHandler.cs @@ -0,0 +1,40 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Data; +using System.IO; +using System.Diagnostics; +using CodeImp.DoomBuilder.Config; +using CodeImp.DoomBuilder.Windows; +using System.Windows.Forms; + +#endregion + +namespace CodeImp.DoomBuilder.Types +{ + [TypeHandler(UniversalType.ThingTag, "Thing Tag", true)] + internal class ThingTagHandler : IntegerHandler + { + } +} diff --git a/Source/Core/Types/TypeHandler.cs b/Source/Core/Types/TypeHandler.cs new file mode 100644 index 0000000..5fc2b65 --- /dev/null +++ b/Source/Core/Types/TypeHandler.cs @@ -0,0 +1,172 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Data; +using System.IO; +using System.Diagnostics; +using System.Windows.Forms; +using CodeImp.DoomBuilder.Config; + +#endregion + +namespace CodeImp.DoomBuilder.Types +{ + /// + /// Type Handler base class. A Type Handler takes care of editing, validating and + /// displaying values of different types for UDMF fields and hexen arguments. + /// + internal abstract class TypeHandler + { + #region ================== Constants + + #endregion + + #region ================== Variables + + protected int index; + protected string typename; + protected bool customusable; + protected bool forargument; + protected ArgumentInfo arginfo; + protected TypeHandlerAttribute attribute; + + #endregion + + #region ================== Properties + + public int Index { get { return index; } } + public string TypeName { get { return typename; } } + public bool IsCustomUsable { get { return customusable; } } + public bool IsForArgument { get { return forargument; } } + public TypeHandlerAttribute Attribute { get { return attribute; } } + + public virtual bool IsBrowseable { get { return false; } } + public virtual bool IsEnumerable { get { return false; } } + public virtual bool IsLimitedToEnums { get { return false; } } + + #endregion + + #region ================== Constructor + + // Constructor + public TypeHandler() + { + } + + // This sets up the handler for arguments + public virtual void SetupArgument(TypeHandlerAttribute attr, ArgumentInfo arginfo) + { + // Setup + this.forargument = true; + this.arginfo = arginfo; + if (attr != null) + { + // Set attributes + this.attribute = attr; + this.index = attr.Index; + this.typename = attr.Name; + this.customusable = attr.IsCustomUsable; + } + else + { + // Indexless + this.attribute = null; + this.index = -1; + this.typename = "Unknown"; + this.customusable = false; + } + } + + // This sets up the handler for arguments + public virtual void SetupField(TypeHandlerAttribute attr, UniversalFieldInfo fieldinfo) + { + // Setup + this.forargument = false; + this.arginfo = arginfo; + if (attr != null) + { + // Set attributes + this.attribute = attr; + this.index = attr.Index; + this.typename = attr.Name; + this.customusable = attr.IsCustomUsable; + } + else + { + // Indexless + this.attribute = null; + this.index = -1; + this.typename = "Unknown"; + this.customusable = false; + } + } + + #endregion + + #region ================== Methods + + // This must set the value + // How the value is actually validated and stored is up to the implementation + public abstract void SetValue(object value); + + // This must return the value as one of the primitive data types + // supported by UDMF: int, string, float or bool + public abstract object GetValue(); + + // This must return the value as integer (for arguments) + public virtual int GetIntValue() + { + throw new NotSupportedException("Override this method to support it as integer for arguments"); + } + + // This must return the value as a string for displaying + public abstract string GetStringValue(); + + // This is called when the user presses the browse button + public virtual void Browse(IWin32Window parent) + { + } + + // This must returns an enum list when IsEnumerable is true + public virtual EnumList GetEnumList() + { + return null; + } + + // String representation + public override string ToString() + { + return this.GetStringValue(); + } + + // This returns the type to display for fixed fields + // Must be a custom usable type + public virtual TypeHandlerAttribute GetDisplayType() + { + return this.attribute; + } + + #endregion + } +} diff --git a/Source/Core/Types/TypeHandlerAttribute.cs b/Source/Core/Types/TypeHandlerAttribute.cs new file mode 100644 index 0000000..8eb42d2 --- /dev/null +++ b/Source/Core/Types/TypeHandlerAttribute.cs @@ -0,0 +1,80 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Data; +using System.IO; +using System.Diagnostics; + +#endregion + +namespace CodeImp.DoomBuilder.Types +{ + internal class TypeHandlerAttribute : Attribute + { + #region ================== Constants + + #endregion + + #region ================== Variables + + private int index; + private string name; + private Type type; + private bool customusable; + + #endregion + + #region ================== Properties + + public int Index { get { return index; } } + public string Name { get { return name; } } + public bool IsCustomUsable { get { return customusable; } } + public Type Type { get { return type; } set { type = value; } } + + #endregion + + #region ================== Constructor / Destructor + + // Constructor + public TypeHandlerAttribute(UniversalType index, string name, bool customusable) + { + // Initialize + this.index = (int)index; + this.name = name; + this.customusable = customusable; + } + + #endregion + + #region ================== Methods + + // String representation + public override string ToString() + { + return name; + } + + #endregion + } +} diff --git a/Source/Core/Types/TypesManager.cs b/Source/Core/Types/TypesManager.cs new file mode 100644 index 0000000..07c2725 --- /dev/null +++ b/Source/Core/Types/TypesManager.cs @@ -0,0 +1,195 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Data; +using System.IO; +using System.Diagnostics; +using CodeImp.DoomBuilder.Config; + +#endregion + +namespace CodeImp.DoomBuilder.Types +{ + internal class TypesManager : IDisposable + { + #region ================== Constants + + #endregion + + #region ================== Variables + + // List of handler types + private Dictionary handlertypes; + + // Disposing + private bool isdisposed = false; + + #endregion + + #region ================== Properties + + public bool IsDisposed { get { return isdisposed; } } + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + public TypesManager() + { + // Initialize + handlertypes = new Dictionary(); + + // Go for all types in this assembly + Type[] types = General.ThisAssembly.GetTypes(); + foreach (Type tp in types) + { + // Check if this type is a class + if (tp.IsClass && !tp.IsAbstract && !tp.IsArray) + { + // Check if class has an TypeHandler attribute + if (Attribute.IsDefined(tp, typeof(TypeHandlerAttribute), false)) + { + // Add the type to the list + object[] attribs = tp.GetCustomAttributes(typeof(TypeHandlerAttribute), false); + TypeHandlerAttribute attr = (attribs[0] as TypeHandlerAttribute); + attr.Type = tp; + handlertypes.Add(attr.Index, attr); + } + } + } + + // We have no destructor + GC.SuppressFinalize(this); + } + + // Disposer + public void Dispose() + { + // Not already disposed? + if (!isdisposed) + { + // Clean up + handlertypes.Clear(); + + // Done + isdisposed = true; + } + } + + #endregion + + #region ================== Methods + + // This returns the type handler for the given argument + public TypeHandler GetArgumentHandler(ArgumentInfo arginfo) + { + Type t = typeof(NullHandler); + TypeHandlerAttribute ta = null; + + // Do we have a handler type for this? + if (handlertypes.ContainsKey(arginfo.Type)) + { + ta = handlertypes[arginfo.Type]; + t = ta.Type; + } + + // Create instance + TypeHandler th = (TypeHandler)General.ThisAssembly.CreateInstance(t.FullName); + th.SetupArgument(ta, arginfo); + return th; + } + + // This returns the type handler for a custom universal field + public TypeHandler GetFieldHandler(int type, object defaultsetting) + { + Type t = typeof(NullHandler); + TypeHandlerAttribute ta = null; + + // Do we have a handler type for this? + if (handlertypes.ContainsKey(type)) + { + ta = handlertypes[type]; + t = ta.Type; + } + + // Create instance + TypeHandler th = (TypeHandler)General.ThisAssembly.CreateInstance(t.FullName); + th.SetupField(ta, null); + th.SetValue(defaultsetting); + return th; + } + + // This returns the type handler for a given universal field + public TypeHandler GetFieldHandler(UniversalFieldInfo fieldinfo) + { + Type t = typeof(NullHandler); + TypeHandlerAttribute ta = null; + + // Do we have a handler type for this? + if (handlertypes.ContainsKey(fieldinfo.Type)) + { + ta = handlertypes[fieldinfo.Type]; + t = ta.Type; + } + + // Create instance + TypeHandler th = (TypeHandler)General.ThisAssembly.CreateInstance(t.FullName); + th.SetupField(ta, fieldinfo); + th.SetValue(fieldinfo.Default); + return th; + } + + // This returns all custom attributes + public TypeHandlerAttribute[] GetCustomUseAttributes() + { + List attribs = new List(); + foreach (KeyValuePair ta in handlertypes) + if (ta.Value.IsCustomUsable) attribs.Add(ta.Value); + return attribs.ToArray(); + } + + // This returns the attribute with the give name + public TypeHandlerAttribute GetNamedAttribute(string name) + { + foreach (KeyValuePair ta in handlertypes) + { + if (ta.Value.Name == name) return ta.Value; + } + + // Nothing found + return null; + } + + // This returns the attribute with the give type + public TypeHandlerAttribute GetAttribute(int type) + { + // Do we have a handler type for this? + if (handlertypes.ContainsKey(type)) return handlertypes[type]; + else return null; + } + + #endregion + } +} diff --git a/Source/Core/Types/UniversalType.cs b/Source/Core/Types/UniversalType.cs new file mode 100644 index 0000000..e24baf5 --- /dev/null +++ b/Source/Core/Types/UniversalType.cs @@ -0,0 +1,51 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.IO; + +#endregion + +namespace CodeImp.DoomBuilder.Types +{ + public enum UniversalType : int + { + Integer = 0, + Float = 1, + String = 2, + Boolean = 3, + LinedefType = 4, + SectorEffect = 5, + Texture = 6, + Flat = 7, + AngleDegrees = 8, + AngleRadians = 9, + Color = 10, + EnumOption = 11, + EnumBits = 12, + SectorTag = 13, + ThingTag = 14, + LinedefTag = 15, + EnumStrings = 16, + AngleDegreesFloat = 17 + } +} diff --git a/Source/Core/VisualModes/IVisualPickable.cs b/Source/Core/VisualModes/IVisualPickable.cs new file mode 100644 index 0000000..31a5073 --- /dev/null +++ b/Source/Core/VisualModes/IVisualPickable.cs @@ -0,0 +1,49 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using System.Drawing; +using System.ComponentModel; +using CodeImp.DoomBuilder.Map; +using SlimDX.Direct3D9; +using SlimDX; +using CodeImp.DoomBuilder.Geometry; +using System.Drawing.Imaging; +using CodeImp.DoomBuilder.Data; +using CodeImp.DoomBuilder.Editing; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Rendering; + +#endregion + +namespace CodeImp.DoomBuilder.VisualModes +{ + public interface IVisualPickable + { + bool Selected { get; set; } + bool PickFastReject(Vector3D from, Vector3D to, Vector3D dir); + bool PickAccurate(Vector3D from, Vector3D to, Vector3D dir, ref float u_ray); + } +} diff --git a/Source/Core/VisualModes/VisualBlockEntry.cs b/Source/Core/VisualModes/VisualBlockEntry.cs new file mode 100644 index 0000000..632edd0 --- /dev/null +++ b/Source/Core/VisualModes/VisualBlockEntry.cs @@ -0,0 +1,72 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using CodeImp.DoomBuilder.Windows; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Actions; +using CodeImp.DoomBuilder.Geometry; +using System.Drawing; +using SlimDX; +using CodeImp.DoomBuilder.Editing; + +#endregion + +namespace CodeImp.DoomBuilder.VisualModes +{ + public sealed class VisualBlockEntry + { + #region ================== Variables + + // Members + private List lines; + private List things; + private List sectors; + + #endregion + + #region ================== Properties + + public List Lines { get { return lines; } } + public List Things { get { return things; } } + public List Sectors { get { return sectors; } } + + #endregion + + #region ================== Constructor + + // Constructor for empty block + internal VisualBlockEntry() + { + lines = new List(2); + things = new List(2); + sectors = new List(2); + } + + #endregion + } +} diff --git a/Source/Core/VisualModes/VisualBlockMap.cs b/Source/Core/VisualModes/VisualBlockMap.cs new file mode 100644 index 0000000..13b1a24 --- /dev/null +++ b/Source/Core/VisualModes/VisualBlockMap.cs @@ -0,0 +1,471 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using CodeImp.DoomBuilder.Windows; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Actions; +using CodeImp.DoomBuilder.Geometry; +using System.Drawing; +using SlimDX; +using CodeImp.DoomBuilder.Editing; + +#endregion + +namespace CodeImp.DoomBuilder.VisualModes +{ + public sealed class VisualBlockMap + { + #region ================== Constants + + public const int BLOCK_SIZE_SHIFT = 7; + public const int BLOCK_SIZE = 1 << BLOCK_SIZE_SHIFT; + public const float BLOCK_RADIUS = BLOCK_SIZE * Angle2D.SQRT2; + + #endregion + + #region ================== Variables + + // Blocks + private Dictionary blockmap; + + // State + private bool isdisposed; + + #endregion + + #region ================== Properties + + public bool IsDisposed { get { return isdisposed; } } + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + internal VisualBlockMap() + { + // Initialize + blockmap = new Dictionary(); + } + + // Disposer + internal void Dispose() + { + // Not already disposed? + if (!isdisposed) + { + // Clean up + blockmap = null; + + // Done + isdisposed = true; + } + } + + #endregion + + #region ================== Methods + + // This returns the block coordinates + public Point GetBlockCoordinates(Vector2D v) + { + return new Point((int)v.x >> BLOCK_SIZE_SHIFT, + (int)v.y >> BLOCK_SIZE_SHIFT); + } + + // This returns the block center in world coordinates + public Vector2D GetBlockCenter(Point p) + { + return new Vector2D((float)((p.X << BLOCK_SIZE_SHIFT) + (BLOCK_SIZE >> 1)), + (float)((p.Y << BLOCK_SIZE_SHIFT) + (BLOCK_SIZE >> 1))); + } + + // This returns the key for a block at the given coordinates + // TODO: Could we just use the Point struct as key? + private ulong GetBlockKey(Point p) + { + return unchecked(((ulong)(uint)p.X << 32) + (ulong)(uint)p.Y); + } + + // This returns the block with the given coordinates + // Creates the block if it doesn't exist yet + public VisualBlockEntry GetBlock(Point p) + { + ulong k = GetBlockKey(p); + if (blockmap.ContainsKey(k)) + { + return blockmap[k]; + } + else + { + return (blockmap[k] = new VisualBlockEntry()); + } + } + + public Sector GetSectorAt(Vector2D pos) + { + List sectors = new List(1); + + foreach (Sector s in GetBlock(GetBlockCoordinates(pos)).Sectors) + if (s.Intersect(pos)) + sectors.Add(s); + + if (sectors.Count == 0) + { + return null; + } + else if (sectors.Count == 1) + { + return sectors[0]; + } + else + { + // Having multiple intersections indicates that there are self-referencing sectors in this spot. + // In this case we have to check which side of the nearest linedef pos is on, and then use that sector + HashSet linedefs = new HashSet(); + + foreach (Sector s in sectors) + foreach (Sidedef sd in s.Sidedefs) + linedefs.Add(sd.Line); + + Linedef nearest = MapSet.NearestLinedef(linedefs, pos); + double d = nearest.SideOfLine(pos); + + if (d <= 0.0 && nearest.Front != null) + return nearest.Front.Sector; + else if (nearest.Back != null) + return nearest.Back.Sector; + } + + return null; + } + + // This clears the blockmap + public void Clear() + { + blockmap = new Dictionary(); + } + + // This returns a range of blocks in a square + public List GetSquareRange(RectangleF rect) + { + // Calculate block coordinates + Point lt = GetBlockCoordinates(new Vector2D(rect.Left, rect.Top)); + Point rb = GetBlockCoordinates(new Vector2D(rect.Right, rect.Bottom)); + + // Go through the range to make a list + int entriescount = (rb.X - lt.X) * (rb.Y - lt.Y); + List entries = new List(entriescount); + for (int x = lt.X; x <= rb.X; x++) + { + for (int y = lt.Y; y <= rb.Y; y++) + { + entries.Add(GetBlock(new Point(x, y))); + } + } + + // Return list + return entries; + } + + // This returns a range of blocks in a frustum + public List GetFrustumRange(ProjectedFrustum2D frustum) + { + // Make square range from frustum circle + // This will be the range in which we will test blocks + Point lt = GetBlockCoordinates(frustum.Center - frustum.Radius); + Point rb = GetBlockCoordinates(frustum.Center + frustum.Radius); + + // Constants we need + float blockfrustumdistance2 = (frustum.Radius * frustum.Radius) + (BLOCK_RADIUS * BLOCK_RADIUS); + + // Go through the range to make a list + int entriescount = (rb.X - lt.X) * (rb.Y - lt.Y); + List entries = new List(entriescount); + for (int x = lt.X; x <= rb.X; x++) + { + for (int y = lt.Y; y <= rb.Y; y++) + { + // First check if the block circle is intersecting the frustum circle + Point block = new Point(x, y); + Vector2D blockcenter = GetBlockCenter(block); + if (Vector2D.DistanceSq(frustum.Center, blockcenter) < blockfrustumdistance2) + { + // Add the block if the block circle is inside the frustum + if (frustum.IntersectCircle(blockcenter, BLOCK_RADIUS)) entries.Add(GetBlock(block)); + } + } + } + + // Return list + return entries; + } + + // This returns all blocks along the given line + public List GetLineBlocks(Vector2D v1, Vector2D v2) + { + float deltax, deltay; + float posx, posy; + Point pos, end; + int dirx, diry; + + // Estimate number of blocks we will go through and create list + int entriescount = (int)(Vector2D.ManhattanDistance(v1, v2) * 2.0f) / BLOCK_SIZE; + List entries = new List(entriescount); + + // Find start and end block + pos = GetBlockCoordinates(v1); + end = GetBlockCoordinates(v2); + + // Add this block + entries.Add(GetBlock(pos)); + + // Moving outside the block? + if (pos != end) + { + // Calculate current block edges + float cl = pos.X * BLOCK_SIZE; + float cr = (pos.X + 1) * BLOCK_SIZE; + float ct = pos.Y * BLOCK_SIZE; + float cb = (pos.Y + 1) * BLOCK_SIZE; + + // Line directions + dirx = Math.Sign(v2.x - v1.x); + diry = Math.Sign(v2.y - v1.y); + + // Calculate offset and delta movement over x + if (dirx >= 0) + { + posx = (cr - v1.x) / (v2.x - v1.x); + deltax = BLOCK_SIZE / (v2.x - v1.x); + } + else + { + // Calculate offset and delta movement over x + posx = (v1.x - cl) / (v1.x - v2.x); + deltax = BLOCK_SIZE / (v1.x - v2.x); + } + + // Calculate offset and delta movement over y + if (diry >= 0) + { + posy = (cb - v1.y) / (v2.y - v1.y); + deltay = BLOCK_SIZE / (v2.y - v1.y); + } + else + { + posy = (v1.y - ct) / (v1.y - v2.y); + deltay = BLOCK_SIZE / (v1.y - v2.y); + } + + // Continue while not reached the end + while (pos != end) + { + // Check in which direction to move + if (posx < posy) + { + // Move horizontally + posx += deltax; + if (pos.X != end.X) pos.X += dirx; + } + else + { + // Move vertically + posy += deltay; + if (pos.Y != end.Y) pos.Y += diry; + } + + // Add lines to this block + entries.Add(GetBlock(pos)); + } + } + + // Return list + return entries; + } + + // This puts a thing in the blockmap + public void AddThingsSet(ICollection things) + { + foreach (Thing t in things) AddThing(t); + } + + // This puts a thing in the blockmap + public void AddThing(Thing t) + { + Point p = GetBlockCoordinates(t.Position); + VisualBlockEntry block = GetBlock(p); + block.Things.Add(t); + } + + // This puts a secotr in the blockmap + public void AddSectorsSet(ICollection sectors) + { + foreach (Sector s in sectors) AddSector(s); + } + + // This puts a sector in the blockmap + public void AddSector(Sector s) + { + Point p1 = GetBlockCoordinates(new Vector2D(s.BBox.Left, s.BBox.Top)); + Point p2 = GetBlockCoordinates(new Vector2D(s.BBox.Right, s.BBox.Bottom)); + for (int x = p1.X; x <= p2.X; x++) + { + for (int y = p1.Y; y <= p2.Y; y++) + { + VisualBlockEntry block = GetBlock(new Point(x, y)); + block.Sectors.Add(s); + } + } + } + + // This puts a whole set of linedefs in the blocks they cross + public void AddLinedefsSet(ICollection lines) + { + foreach (Linedef l in lines) AddLinedef(l); + } + + // This puts a single linedef in all blocks it crosses + public void AddLinedef(Linedef line) + { + Vector2D v1, v2; + float deltax, deltay; + float posx, posy; + Point pos, end; + int dirx, diry; + + // Get coordinates + v1 = line.Start.Position; + v2 = line.End.Position; + + // Find start and end block + pos = GetBlockCoordinates(v1); + end = GetBlockCoordinates(v2); + + // Horizontal straight line? + if (pos.Y == end.Y) + { + // Simple loop + dirx = Math.Sign(v2.x - v1.x); + for (int x = pos.X; x != end.X; x += dirx) + { + GetBlock(new Point(x, pos.Y)).Lines.Add(line); + } + GetBlock(end).Lines.Add(line); + } + // Vertical straight line? + else if (pos.X == end.X) + { + // Simple loop + diry = Math.Sign(v2.y - v1.y); + for (int y = pos.Y; y != end.Y; y += diry) + { + GetBlock(new Point(pos.X, y)).Lines.Add(line); + } + GetBlock(end).Lines.Add(line); + } + else + { + // Add lines to this block + GetBlock(pos).Lines.Add(line); + + // Moving outside the block? + if (pos != end) + { + // Calculate current block edges + float cl = pos.X * BLOCK_SIZE; + float cr = (pos.X + 1) * BLOCK_SIZE; + float ct = pos.Y * BLOCK_SIZE; + float cb = (pos.Y + 1) * BLOCK_SIZE; + + // Line directions + dirx = Math.Sign(v2.x - v1.x); + diry = Math.Sign(v2.y - v1.y); + + // Calculate offset and delta movement over x + if (dirx == 0) + { + posx = float.MaxValue; + deltax = float.MaxValue; + } + else if (dirx > 0) + { + posx = (cr - v1.x) / (v2.x - v1.x); + deltax = BLOCK_SIZE / (v2.x - v1.x); + } + else + { + // Calculate offset and delta movement over x + posx = (v1.x - cl) / (v1.x - v2.x); + deltax = BLOCK_SIZE / (v1.x - v2.x); + } + + // Calculate offset and delta movement over y + if (diry == 0) + { + posy = float.MaxValue; + deltay = float.MaxValue; + } + else if (diry > 0) + { + posy = (cb - v1.y) / (v2.y - v1.y); + deltay = BLOCK_SIZE / (v2.y - v1.y); + } + else + { + posy = (v1.y - ct) / (v1.y - v2.y); + deltay = BLOCK_SIZE / (v1.y - v2.y); + } + + // Continue while not reached the end + while (pos != end) + { + // Check in which direction to move + if (posx < posy) + { + // Move horizontally + posx += deltax; + if (pos.X != end.X) pos.X += dirx; + } + else + { + // Move vertically + posy += deltay; + if (pos.Y != end.Y) pos.Y += diry; + } + + // Add lines to this block + GetBlock(pos).Lines.Add(line); + } + } + } + } + + #endregion + } +} diff --git a/Source/Core/VisualModes/VisualCamera.cs b/Source/Core/VisualModes/VisualCamera.cs new file mode 100644 index 0000000..5a822ce --- /dev/null +++ b/Source/Core/VisualModes/VisualCamera.cs @@ -0,0 +1,171 @@ +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using CodeImp.DoomBuilder.Geometry; +using CodeImp.DoomBuilder.Map; + +#endregion + +namespace CodeImp.DoomBuilder.VisualModes +{ + /// + /// This class provides the camera in Visual Mode + /// + public class VisualCamera + { + #region ================== Constants + + private const float ANGLE_FROM_MOUSE = 0.0001f; + public const float MAX_ANGLEZ_LOW = 91f / Angle2D.PIDEG; + public const float MAX_ANGLEZ_HIGH = (360f - 91f) / Angle2D.PIDEG; + public const float THING_Z_OFFSET = 41.0f; + + #endregion + + #region ================== Variables + + // Properties + private Vector3D position; + private Vector3D target; + private Vector3D movemultiplier; + private float anglexy, anglez; + private Sector sector; + + #endregion + + #region ================== Properties + + public Vector3D Position { get { return position; } set { position = value; } } + public Vector3D Target { get { return target; } } + public float AngleXY { get { return anglexy; } set { anglexy = value; } } + public float AngleZ { get { return anglez; } set { anglez = value; } } + public Sector Sector { get { return sector; } internal set { sector = value; } } + public Vector3D MoveMultiplier { get { return movemultiplier; } set { movemultiplier = value; } } + + #endregion + + #region ================== Constructor / Destructor + + // Constructor + public VisualCamera() + { + // Initialize + this.movemultiplier = new Vector3D(1.0f, 1.0f, 1.0f); + this.position = position; + this.anglexy = 0.0f; + this.anglez = Angle2D.PI; + this.sector = null; + + PositionAtThing(); + } + + #endregion + + #region ================== Methods + + // Mouse input + internal void ProcessMouseInput(Vector2D delta) + { + // Change camera angles with the mouse changes + anglexy -= delta.x * ANGLE_FROM_MOUSE; + if (General.Settings.InvertYAxis) + anglez -= delta.y * ANGLE_FROM_MOUSE; + else + anglez += delta.y * ANGLE_FROM_MOUSE; + + // Normalize angles + anglexy = Angle2D.Normalized(anglexy); + anglez = Angle2D.Normalized(anglez); + + // Limit vertical angle + if (anglez < MAX_ANGLEZ_LOW) anglez = MAX_ANGLEZ_LOW; + if (anglez > MAX_ANGLEZ_HIGH) anglez = MAX_ANGLEZ_HIGH; + } + + // Key input + internal void ProcessMovement(Vector3D deltavec) + { + // Calculate camera direction vectors + Vector3D camvec = Vector3D.FromAngleXYZ(anglexy, anglez); + + // Position the camera + position += deltavec; + + // Target the camera + target = position + camvec; + } + + // This applies the position and angle from the 3D Camera Thing + // Returns false when it couldn't find a 3D Camera Thing + public virtual bool PositionAtThing() + { + Thing modething = null; + Vector3D delta; + + // Find a 3D Mode thing + foreach (Thing t in General.Map.Map.Things) + if (t.Type == General.Map.Config.Start3DModeThingType) modething = t; + + // Found one? + if (modething != null) + { + modething.DetermineSector(); + float z = modething.Position.z; + if (modething.Sector != null) + z = modething.Position.z + (float)modething.Sector.FloorHeight; + + // Position camera here + Vector3D wantedposition = new Vector3D(modething.Position.x, modething.Position.y, z + THING_Z_OFFSET); + delta = position - wantedposition; + if (delta.GetLength() > 1.0f) position = wantedposition; + + // Change angle + float wantedanglexy = modething.Angle + Angle2D.PI; + if (anglexy != wantedanglexy) + { + anglexy = wantedanglexy; + anglez = Angle2D.PI; + } + return true; + } + else + { + return false; + } + } + + // This applies the camera position and angle to the 3D Camera Thing + // Returns false when it couldn't find a 3D Camera Thing + public virtual bool ApplyToThing() + { + Thing modething = null; + + // Find a 3D Mode thing + foreach (Thing t in General.Map.Map.Things) + if (t.Type == General.Map.Config.Start3DModeThingType) modething = t; + + // Found one? + if (modething != null) + { + int z = 0; + if (sector != null) + z = (int)position.z - sector.FloorHeight; + + // Position the thing to match camera + modething.Move((int)position.x, (int)position.y, z - THING_Z_OFFSET); + modething.Rotate(anglexy - Angle2D.PI); + return true; + } + else + { + return false; + } + } + + #endregion + } +} diff --git a/Source/Core/VisualModes/VisualGeometry.cs b/Source/Core/VisualModes/VisualGeometry.cs new file mode 100644 index 0000000..5b2ceaf --- /dev/null +++ b/Source/Core/VisualModes/VisualGeometry.cs @@ -0,0 +1,195 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using System.Drawing; +using System.ComponentModel; +using CodeImp.DoomBuilder.Map; +using SlimDX.Direct3D9; +using SlimDX; +using CodeImp.DoomBuilder.Geometry; +using System.Drawing.Imaging; +using CodeImp.DoomBuilder.Data; +using CodeImp.DoomBuilder.Editing; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Rendering; + +#endregion + +namespace CodeImp.DoomBuilder.VisualModes +{ + public abstract class VisualGeometry : IVisualPickable, IComparable + { + #region ================== Variables + + // Texture + private ImageData texture; + + // Vertices + private WorldVertex[] vertices; + private int triangles; + + // Desired modulate color + private PixelColor modulatecolor; + private Color4 modcolor4; + + // Selected? + protected bool selected; + + // Elements that this geometry is bound to + // Only the sector is required, sidedef is only for walls + private VisualSector sector; + private Sidedef sidedef; + + /// + /// Absolute intersecting coordinates are set during object picking. This is not set if the geometry is not bound to a sidedef. + /// + protected Vector3D pickintersect; + + /// + /// Distance unit along the object picking ray is set during object picking. (0.0 is at camera, 1.0f is at far plane) This is not set if the geometry is not bound to a sidedef. + /// + protected float pickrayu; + + // Rendering + private int renderpass = (int)RenderPass.Solid; + + // Sector buffer info + private int vertexoffset; + + #endregion + + #region ================== Properties + + // Internal properties + internal WorldVertex[] Vertices { get { return vertices; } } + internal int VertexOffset { get { return vertexoffset; } set { vertexoffset = value; } } + internal int Triangles { get { return triangles; } } + internal int RenderPassInt { get { return renderpass; } } + internal Color4 ModColor4 { get { return modcolor4; } } + + /// + /// Render pass in which this geometry must be rendered. Default is Solid. + /// + public RenderPass RenderPass { get { return (RenderPass)renderpass; } set { renderpass = (int)value; } } + + /// + /// Image to use as texture on this geometry. + /// + public ImageData Texture { get { return texture; } set { texture = value; } } + + /// + /// Color to modulate the texture pixels with. + /// + public PixelColor ModulateColor { get { return modulatecolor; } set { modcolor4 = value.ToColorValue(); modulatecolor = value; } } + + /// + /// Returns the VisualSector this geometry has been added to. + /// + public VisualSector Sector { get { return sector; } internal set { sector = value; } } + + /// + /// Returns the Sidedef that this geometry is created for. Null for geometry that is sector-wide. + /// + public Sidedef Sidedef { get { return sidedef; } } + + /// + /// Selected or not? This is only used by the core to determine what color to draw it with. + /// + public bool Selected { get { return selected; } set { selected = value; } } + + #endregion + + #region ================== Constructor / Destructor + + /// + /// This creates sector-global visual geometry. This geometry is always visible when any of the sector is visible. + /// + public VisualGeometry(VisualSector vs) + { + this.sector = vs; + this.ModulateColor = new PixelColor(255, 255, 255, 255); + } + + /// + /// This creates visual geometry that is bound to a sidedef. This geometry is only visible when the sidedef is visible. It is automatically back-face culled during rendering and automatically XY intersection tested as well as back-face culled during object picking. + /// + /// + public VisualGeometry(VisualSector vs, Sidedef sd) + { + this.sector = vs; + this.sidedef = sd; + this.ModulateColor = new PixelColor(255, 255, 255, 255); + } + + #endregion + + #region ================== Methods + + // This sets the vertices for this geometry + protected void SetVertices(ICollection verts) + { + // Copy vertices + vertices = new WorldVertex[verts.Count]; + verts.CopyTo(vertices, 0); + triangles = vertices.Length / 3; + if (sector != null) sector.NeedsUpdateGeo = true; + } + + // This compares for sorting by sector + public int CompareTo(VisualGeometry other) + { + // Compare sectors + return this.sector.Sector.FixedIndex - other.sector.Sector.FixedIndex; + } + + // This keeps the results for a sidedef intersection + internal void SetPickResults(Vector3D intersect, float u) + { + this.pickintersect = intersect; + this.pickrayu = u; + } + + /// + /// This is called when the geometry must be tested for line intersection. This should reject + /// as fast as possible to rule out all geometry that certainly does not touch the line. + /// + public virtual bool PickFastReject(Vector3D from, Vector3D to, Vector3D dir) + { + return false; + } + + /// + /// This is called when the geometry must be tested for line intersection. This should perform + /// accurate hit detection and set u_ray to the position on the ray where this hits the geometry. + /// + public virtual bool PickAccurate(Vector3D from, Vector3D to, Vector3D dir, ref float u_ray) + { + return false; + } + + #endregion + } +} diff --git a/Source/Core/VisualModes/VisualMode.cs b/Source/Core/VisualModes/VisualMode.cs new file mode 100644 index 0000000..7dd8e7f --- /dev/null +++ b/Source/Core/VisualModes/VisualMode.cs @@ -0,0 +1,897 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using CodeImp.DoomBuilder.Windows; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Actions; +using CodeImp.DoomBuilder.Geometry; +using System.Drawing; +using SlimDX; +using CodeImp.DoomBuilder.Editing; + +#endregion + +namespace CodeImp.DoomBuilder.VisualModes +{ + /// + /// Provides specialized functionality for a visual (3D) Doom Builder editing mode. + /// + public abstract class VisualMode : EditMode + { + #region ================== Constants + + private const double MOVE_SPEED_MULTIPLIER = 0.001d; + + #endregion + + #region ================== Variables + + // 3D Mode thing + protected Thing modething; + + // Graphics + protected IRenderer3D renderer; + private Renderer3D renderer3d; + + // Options + private bool processgeometry; + private bool processthings; + + // Input + private bool keyforward; + private bool keybackward; + private bool keyleft; + private bool keyright; + private bool keyup; + private bool keydown; + + private static Vector2D initialCameraPosition; + private bool keepPreviousPosition; + + // Map + protected VisualBlockMap blockmap; + protected Dictionary allthings; + protected Dictionary allsectors; + protected List visibleblocks; + protected List visiblethings; + protected Dictionary visiblesectors; + protected List visiblegeometry; + + #endregion + + #region ================== Properties + + public bool ProcessGeometry { get { return processgeometry; } set { processgeometry = value; } } + public bool ProcessThings { get { return processthings; } set { processthings = value; } } + public VisualBlockMap BlockMap { get { return blockmap; } } + + #endregion + + #region ================== Constructor / Disposer + + /// + /// Provides specialized functionality for a visual (3D) Doom Builder editing mode. + /// + public VisualMode() + { + // Initialize + this.renderer = General.Map.Renderer3D; + this.renderer3d = (Renderer3D)General.Map.Renderer3D; + this.blockmap = new VisualBlockMap(); + this.allsectors = new Dictionary(General.Map.Map.Sectors.Count); + this.allthings = new Dictionary(General.Map.Map.Things.Count); + this.visibleblocks = new List(); + this.visiblesectors = new Dictionary(50); + this.visiblegeometry = new List(200); + this.visiblethings = new List(100); + this.processgeometry = true; + this.processthings = true; + + if ( (ClassicMode) General.Editing.Mode is ClassicMode) + { + ClassicMode oldMode = (ClassicMode) General.Editing.Mode; + + if (oldMode.IsMouseInside) + { + initialCameraPosition = new Vector2D(oldMode.MouseMapPos.x, oldMode.MouseMapPos.y); + keepPreviousPosition = false; + } + else + { + keepPreviousPosition = true; + } + } + } + + // Disposer + public override void Dispose() + { + // Not already disposed? + if (!isdisposed) + { + // Clean up + foreach (KeyValuePair s in allsectors) s.Value.Dispose(); + blockmap.Dispose(); + visiblesectors = null; + visiblegeometry = null; + visibleblocks = null; + visiblethings = null; + allsectors = null; + allthings = null; + blockmap = null; + + // Done + base.Dispose(); + } + } + + #endregion + + #region ================== Start / Stop + + // Mode is engaged + public override void OnEngage() + { + base.OnEngage(); + + // Update the used textures + General.Map.Data.UpdateUsedTextures(); + + // Fill the blockmap + FillBlockMap(); + + if (!keepPreviousPosition) + { + //If initial position is inside or nearby a sector - adjust camera.z accordingly + float posz = General.Map.VisualCamera.Position.z; + Sector nearestSector = General.Map.Map.GetSectorByCoordinates(initialCameraPosition, blockmap); + + if (nearestSector == null) + { + Linedef nearestline = MapSet.NearestLinedef(General.Map.Map.Linedefs, initialCameraPosition); + if (nearestline != null) + { + double side = nearestline.SideOfLine(initialCameraPosition); + Sidedef nearestside = (side < 0.0f ? nearestline.Front : nearestline.Back) ?? (side < 0.0f ? nearestline.Back : nearestline.Front); + if (nearestside != null) nearestSector = nearestside.Sector; + } + } + + if (nearestSector != null) + { + int sectorheight = nearestSector.CeilHeight - nearestSector.FloorHeight; + if (sectorheight < 56) + posz = nearestSector.FloorHeight + Math.Max(16, sectorheight / 2); + else if (General.Map.VisualCamera.Position.z < nearestSector.FloorHeight + 56) + posz = nearestSector.FloorHeight + 56; // same as in DOOM 64 + else if (General.Map.VisualCamera.Position.z > nearestSector.CeilHeight) + posz = nearestSector.CeilHeight - 4; + } + + General.Map.VisualCamera.Position = new Vector3D(initialCameraPosition.x, initialCameraPosition.y, posz); + } + else + { + General.Map.VisualCamera.PositionAtThing(); + } + + + + // Start special input mode + General.Interface.EnableProcessing(); + General.Interface.StartExclusiveMouseInput(); + } + + // Mode is disengaged + public override void OnDisengage() + { + base.OnDisengage(); + + // Dispose + foreach (KeyValuePair vs in allsectors) + vs.Value.Dispose(); + + // Dispose + foreach (KeyValuePair vt in allthings) + vt.Value.Dispose(); + + // Apply camera position to thing + General.Map.VisualCamera.ApplyToThing(); + + // Do not leave the sector on the camera + General.Map.VisualCamera.Sector = null; + + // Stop special input mode + General.Interface.DisableProcessing(); + General.Interface.StopExclusiveMouseInput(); + } + + #endregion + + #region ================== Events + + public override bool OnUndoBegin() + { + renderer.SetCrosshairBusy(true); + General.Interface.RedrawDisplay(); + return base.OnUndoBegin(); + } + + public override void OnUndoEnd() + { + base.OnUndoEnd(); + ResourcesReloadedPartial(); + renderer.SetCrosshairBusy(false); + } + + public override bool OnRedoBegin() + { + renderer.SetCrosshairBusy(true); + General.Interface.RedrawDisplay(); + return base.OnRedoBegin(); + } + + public override void OnRedoEnd() + { + base.OnRedoEnd(); + ResourcesReloadedPartial(); + renderer.SetCrosshairBusy(false); + } + + public override void OnReloadResources() + { + base.OnReloadResources(); + ResourcesReloaded(); + } + + #endregion + + #region ================== Input + + // Mouse input + public override void OnMouseInput(Vector2D delta) + { + base.OnMouseInput(delta); + General.Map.VisualCamera.ProcessMouseInput(delta); + } + + [BeginAction("moveforward", BaseAction = true)] + public virtual void BeginMoveForward() + { + keyforward = true; + } + + [EndAction("moveforward", BaseAction = true)] + public virtual void EndMoveForward() + { + keyforward = false; + } + + [BeginAction("movebackward", BaseAction = true)] + public virtual void BeginMoveBackward() + { + keybackward = true; + } + + [EndAction("movebackward", BaseAction = true)] + public virtual void EndMoveBackward() + { + keybackward = false; + } + + [BeginAction("moveleft", BaseAction = true)] + public virtual void BeginMoveLeft() + { + keyleft = true; + } + + [EndAction("moveleft", BaseAction = true)] + public virtual void EndMoveLeft() + { + keyleft = false; + } + + [BeginAction("moveright", BaseAction = true)] + public virtual void BeginMoveRight() + { + keyright = true; + } + + [EndAction("moveright", BaseAction = true)] + public virtual void EndMoveRight() + { + keyright = false; + } + + [BeginAction("moveup", BaseAction = true)] + public virtual void BeginMoveUp() + { + keyup = true; + } + + [EndAction("moveup", BaseAction = true)] + public virtual void EndMoveUp() + { + keyup = false; + } + + [BeginAction("movedown", BaseAction = true)] + public virtual void BeginMoveDown() + { + keydown = true; + } + + [EndAction("movedown", BaseAction = true)] + public virtual void EndMoveDown() + { + keydown = false; + } + + #endregion + + #region ================== Visibility Culling + + // This preforms visibility culling + protected void DoCulling() + { + Dictionary visiblelines = new Dictionary(200); + Vector2D campos2d = (Vector2D)General.Map.VisualCamera.Position; + float viewdist = General.Settings.ViewDistance; + + // Make collections + visiblesectors = new Dictionary(visiblesectors.Count); + visiblegeometry = new List(visiblegeometry.Capacity); + visiblethings = new List(visiblethings.Capacity); + + // Get the blocks within view range + visibleblocks = blockmap.GetFrustumRange(renderer.Frustum2D); + + // Fill collections with geometry and things + foreach (VisualBlockEntry block in visibleblocks) + { + if (processgeometry) + { + // Lines + foreach (Linedef ld in block.Lines) + { + // Line not already processed? + if (!visiblelines.ContainsKey(ld)) + { + // Add line if not added yet + visiblelines.Add(ld, ld); + + // Which side of the line is the camera on? + if (ld.SideOfLine(campos2d) < 0) + { + // Do front of line + if (ld.Front != null) ProcessSidedefCulling(ld.Front); + } + else + { + // Do back of line + if (ld.Back != null) ProcessSidedefCulling(ld.Back); + } + } + } + } + + if (processthings) + { + // Things + foreach (Thing t in block.Things) + { + VisualThing vt; + + // Not filtered out? + if (General.Map.ThingsFilter.IsThingVisible(t)) + { + if (allthings.ContainsKey(t)) + { + vt = allthings[t]; + } + else + { + // Create new visual thing + vt = CreateVisualThing(t); + if (vt != null) allthings.Add(t, vt); + } + + if (vt != null) + { + visiblethings.Add(vt); + } + } + } + } + } + + if (processgeometry) + { + // Find camera sector + Linedef nld = MapSet.NearestLinedef(visiblelines.Values, campos2d); + if (nld != null) + { + General.Map.VisualCamera.Sector = GetCameraSectorFromLinedef(nld); + } + else + { + // Exceptional case: no lines found in any nearby blocks! + // This could happen in the middle of an extremely large sector and in this case + // the above code will not have found any sectors/sidedefs for rendering. + // Here we handle this special case with brute-force. Let's find the sector + // the camera is in by searching the entire map and render that sector only. + nld = General.Map.Map.NearestLinedef(campos2d); + if (nld != null) + { + General.Map.VisualCamera.Sector = GetCameraSectorFromLinedef(nld); + if (General.Map.VisualCamera.Sector != null) + { + foreach (Sidedef sd in General.Map.VisualCamera.Sector.Sidedefs) + { + float side = sd.Line.SideOfLine(campos2d); + if (((side < 0) && sd.IsFront) || + ((side > 0) && !sd.IsFront)) + ProcessSidedefCulling(sd); + } + } + else + { + // Too far away from the map to see anything + General.Map.VisualCamera.Sector = null; + } + } + else + { + // Map is empty + General.Map.VisualCamera.Sector = null; + } + } + } + } + + // This finds and adds visible sectors + private void ProcessSidedefCulling(Sidedef sd) + { + VisualSector vs; + + // Find the visualsector and make it if needed + if (allsectors.ContainsKey(sd.Sector)) + { + // Take existing visualsector + vs = allsectors[sd.Sector]; + } + else + { + // Make new visualsector + vs = CreateVisualSector(sd.Sector); + if (vs != null) allsectors.Add(sd.Sector, vs); + } + + if (vs != null) + { + // Add to visible sectors if not added yet + if (!visiblesectors.ContainsKey(sd.Sector)) + { + visiblesectors.Add(sd.Sector, vs); + visiblegeometry.AddRange(vs.FixedGeometry); + } + + // Add sidedef geometry + visiblegeometry.AddRange(vs.GetSidedefGeometry(sd)); + } + } + + // This returns the camera sector from linedef + private Sector GetCameraSectorFromLinedef(Linedef ld) + { + if (ld.SideOfLine(General.Map.VisualCamera.Position) < 0) + { + if (ld.Front != null) + return ld.Front.Sector; + else + return null; + } + else + { + if (ld.Back != null) + return ld.Back.Sector; + else + return null; + } + } + + #endregion + + #region ================== Object Picking + + // This picks an object from the scene + public VisualPickResult PickObject(Vector3D from, Vector3D to) + { + VisualPickResult result = new VisualPickResult(); + Line2D ray2d = new Line2D(from, to); + Vector3D delta = to - from; + + // Setup no result + result.picked = null; + result.hitpos = new Vector3D(); + result.u_ray = 1.0f; + + // Find all blocks we are intersecting + List blocks = blockmap.GetLineBlocks(from, to); + + // Make collections + Dictionary lines = new Dictionary(blocks.Count * 10); + Dictionary sectors = new Dictionary(blocks.Count * 10); + List pickables = new List(blocks.Count * 10); + + // Add geometry from the camera sector + if ((General.Map.VisualCamera.Sector != null) && allsectors.ContainsKey(General.Map.VisualCamera.Sector)) + { + VisualSector vs = allsectors[General.Map.VisualCamera.Sector]; + sectors.Add(General.Map.VisualCamera.Sector, vs); + foreach (VisualGeometry g in vs.FixedGeometry) pickables.Add(g); + } + + // Go for all lines to see which ones we intersect + // We will collect geometry from the sectors and sidedefs + foreach (VisualBlockEntry b in blocks) + { + foreach (Linedef ld in b.Lines) + { + // Make sure we don't test a line twice + if (!lines.ContainsKey(ld)) + { + lines.Add(ld, ld); + + // Intersecting? + float u; + if (ld.Line.GetIntersection(ray2d, out u)) + { + // Check on which side we are + float side = ld.SideOfLine(ray2d.v1); + + // Calculate intersection point + Vector3D intersect = from + delta * u; + + // We must add the sectors of both sides of the line + // If we wouldn't, then aiming at a sector that is just within range + // could result in an incorrect hit (because the far line of the + // sector may not be included in this loop) + if (ld.Front != null) + { + // Find the visualsector + if (allsectors.ContainsKey(ld.Front.Sector)) + { + VisualSector vs = allsectors[ld.Front.Sector]; + + // Add sector if not already added + if (!sectors.ContainsKey(ld.Front.Sector)) + { + sectors.Add(ld.Front.Sector, vs); + foreach (VisualGeometry g in vs.FixedGeometry) + { + // Must have content + if (g.Triangles > 0) + pickables.Add(g); + } + } + + // Add sidedef if on the front side + if (side < 0.0f) + { + List sidedefgeo = vs.GetSidedefGeometry(ld.Front); + foreach (VisualGeometry g in sidedefgeo) + { + // Must have content + if (g.Triangles > 0) + { + g.SetPickResults(intersect, u); + pickables.Add(g); + } + } + } + } + } + + // Add back side also + if (ld.Back != null) + { + // Find the visualsector + if (allsectors.ContainsKey(ld.Back.Sector)) + { + VisualSector vs = allsectors[ld.Back.Sector]; + + // Add sector if not already added + if (!sectors.ContainsKey(ld.Back.Sector)) + { + sectors.Add(ld.Back.Sector, vs); + foreach (VisualGeometry g in vs.FixedGeometry) + { + // Must have content + if (g.Triangles > 0) + pickables.Add(g); + } + } + + // Add sidedef if on the front side + if (side > 0.0f) + { + List sidedefgeo = vs.GetSidedefGeometry(ld.Back); + foreach (VisualGeometry g in sidedefgeo) + { + // Must have content + if (g.Triangles > 0) + { + g.SetPickResults(intersect, u); + pickables.Add(g); + } + } + } + } + } + } + } + } + } + + // Add all the visible things + foreach (VisualThing vt in visiblethings) + pickables.Add(vt); + + // Now we have a list of potential geometry that lies along the trace line. + // We still don't know what geometry actually hits, but we ruled out that which doesn't get even close. + // This is still too much for accurate intersection testing, so we do a fast reject pass first. + Vector3D direction = to - from; + direction = direction.GetNormal(); + List potentialpicks = new List(pickables.Count); + foreach (IVisualPickable p in pickables) + { + if (p.PickFastReject(from, to, direction)) potentialpicks.Add(p); + } + + // Now we do an accurate intersection test for all resulting geometry + // We keep only the closest hit! + foreach (IVisualPickable p in potentialpicks) + { + float u = result.u_ray; + if (p.PickAccurate(from, to, direction, ref u)) + { + // Closer than previous find? + if ((u > 0.0f) && (u < result.u_ray)) + { + result.u_ray = u; + result.picked = p; + } + } + } + + // Setup final result + result.hitpos = from + to * result.u_ray; + + // Done + return result; + } + + #endregion + + #region ================== Processing + + /// + /// This disposes all resources. Needed geometry will be rebuild automatically. + /// + protected virtual void ResourcesReloaded() + { + // Dispose + foreach (KeyValuePair vs in allsectors) + vs.Value.Dispose(); + + foreach (KeyValuePair vt in allthings) + vt.Value.Dispose(); + + // Clear collections + allsectors.Clear(); + allthings.Clear(); + visiblesectors.Clear(); + visibleblocks.Clear(); + visiblegeometry.Clear(); + visiblethings.Clear(); + + // Make new blockmap + FillBlockMap(); + + // Visibility culling (this re-creates the needed resources) + DoCulling(); + } + + /// + /// This disposes orphaned resources and resources on changed geometry. + /// This usually happens when geometry is changed by undo, redo, cut or paste actions + /// and uses the marks to check what needs to be reloaded. + /// + protected virtual void ResourcesReloadedPartial() + { + Dictionary newsectors = new Dictionary(allsectors.Count); + + // Neighbour sectors must be updated as well + foreach (Sector s in General.Map.Map.Sectors) + { + if (s.Marked) + { + foreach (Sidedef sd in s.Sidedefs) + if (sd.Other != null) sd.Other.Marked = true; + } + } + + // Go for all sidedefs to mark sectors that need updating + foreach (Sidedef sd in General.Map.Map.Sidedefs) + if (sd.Marked) sd.Sector.Marked = true; + + // Go for all vertices to mark linedefs that need updating + foreach (Vertex v in General.Map.Map.Vertices) + { + if (v.Marked) + { + foreach (Linedef ld in v.Linedefs) + ld.Marked = true; + } + } + + // Go for all linedefs to mark sectors that need updating + foreach (Linedef ld in General.Map.Map.Linedefs) + { + if (ld.Marked) + { + if (ld.Front != null) ld.Front.Sector.Marked = true; + if (ld.Back != null) ld.Back.Sector.Marked = true; + } + } + + // Dispose if source was disposed or marked + foreach (KeyValuePair vs in allsectors) + { + if (vs.Key.IsDisposed || vs.Key.Marked) + vs.Value.Dispose(); + else + newsectors.Add(vs.Key, vs.Value); + } + + // Things depend on the sector they are in and because we can't + // easily determine which ones changed, we dispose all things + foreach (KeyValuePair vt in allthings) + vt.Value.Dispose(); + + // Apply new lists + allsectors = newsectors; + allthings = new Dictionary(allthings.Count); + + // Clear visibility collections + visiblesectors.Clear(); + visibleblocks.Clear(); + visiblegeometry.Clear(); + visiblethings.Clear(); + + // Make new blockmap + FillBlockMap(); + + // Visibility culling (this re-creates the needed resources) + DoCulling(); + } + + /// + /// Implement this to create an instance of your VisualSector implementation. + /// + protected abstract VisualSector CreateVisualSector(Sector s); + + /// + /// Implement this to create an instance of your VisualThing implementation. + /// + protected abstract VisualThing CreateVisualThing(Thing t); + + /// + /// This returns the VisualSector for the given Sector. + /// + public VisualSector GetVisualSector(Sector s) { return allsectors[s]; } + + /// + /// This returns the VisualThing for the given Thing. + /// + public VisualThing GetVisualThing(Thing t) { return allthings[t]; } + + /// + /// Returns True when a VisualSector has been created for the specified Sector. + /// + public bool VisualSectorExists(Sector s) { return allsectors.ContainsKey(s); } + + /// + /// Returns True when a VisualThing has been created for the specified Thing. + /// + public bool VisualThingExists(Thing t) { return allthings.ContainsKey(t); } + + /// + /// This is called when the blockmap needs to be refilled, because it was invalidated. + /// This usually happens when geometry is changed by undo, redo, cut or paste actions. + /// Lines and Things are added to the block map by the base implementation. + /// + protected virtual void FillBlockMap() + { + if (blockmap != null) blockmap.Dispose(); + blockmap = new VisualBlockMap(); + + blockmap.AddLinedefsSet(General.Map.Map.Linedefs); + blockmap.AddThingsSet(General.Map.Map.Things); + blockmap.AddSectorsSet(General.Map.Map.Sectors); + } + + /// + /// While this mode is active, this is called continuously to process whatever needs processing. + /// + public override void OnProcess(double deltatime) + { + double multiplier; + + base.OnProcess(deltatime); + + // Camera vectors + Vector3D camvec = Vector3D.FromAngleXYZ(General.Map.VisualCamera.AngleXY, General.Map.VisualCamera.AngleZ); + Vector3D camvecstrafe = Vector3D.FromAngleXY(General.Map.VisualCamera.AngleXY + Angle2D.PIHALF); + Vector3D cammovemul = General.Map.VisualCamera.MoveMultiplier; + Vector3D camdeltapos = new Vector3D(); + Vector3D upvec = new Vector3D(0.0f, 0.0f, 1.0f); + + // Move the camera + if (General.Interface.ShiftState) multiplier = MOVE_SPEED_MULTIPLIER * 2.0f; else multiplier = MOVE_SPEED_MULTIPLIER; + if (keyforward) camdeltapos += camvec * cammovemul * (float)((double)General.Settings.MoveSpeed * multiplier * deltatime); + if (keybackward) camdeltapos -= camvec * cammovemul * (float)((double)General.Settings.MoveSpeed * multiplier * deltatime); + if (keyleft) camdeltapos -= camvecstrafe * cammovemul * (float)((double)General.Settings.MoveSpeed * multiplier * deltatime); + if (keyright) camdeltapos += camvecstrafe * cammovemul * (float)((double)General.Settings.MoveSpeed * multiplier * deltatime); + if (keyup) camdeltapos += upvec * cammovemul * (float)((double)General.Settings.MoveSpeed * multiplier * deltatime); + if (keydown) camdeltapos += -upvec * cammovemul * (float)((double)General.Settings.MoveSpeed * multiplier * deltatime); + + // Move the camera + General.Map.VisualCamera.ProcessMovement(camdeltapos); + + // Apply new camera matrices + renderer.PositionAndLookAt(General.Map.VisualCamera.Position, General.Map.VisualCamera.Target); + + // Visibility culling + DoCulling(); + + // Update labels in main window + General.MainWindow.UpdateCoordinates((Vector2D)General.Map.VisualCamera.Position); + + // Now redraw + General.Interface.RedrawDisplay(); + } + + #endregion + + #region ================== Actions + + #endregion + } +} diff --git a/Source/Core/VisualModes/VisualPickResult.cs b/Source/Core/VisualModes/VisualPickResult.cs new file mode 100644 index 0000000..f766a6a --- /dev/null +++ b/Source/Core/VisualModes/VisualPickResult.cs @@ -0,0 +1,48 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using CodeImp.DoomBuilder.Windows; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Actions; +using CodeImp.DoomBuilder.Geometry; +using System.Drawing; +using SlimDX; +using CodeImp.DoomBuilder.Editing; + +#endregion + +namespace CodeImp.DoomBuilder.VisualModes +{ + public struct VisualPickResult + { + // Members + public IVisualPickable picked; + public float u_ray; + public Vector3D hitpos; + } +} diff --git a/Source/Core/VisualModes/VisualSector.cs b/Source/Core/VisualModes/VisualSector.cs new file mode 100644 index 0000000..18fb8a4 --- /dev/null +++ b/Source/Core/VisualModes/VisualSector.cs @@ -0,0 +1,215 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using System.Drawing; +using System.ComponentModel; +using CodeImp.DoomBuilder.Map; +using SlimDX.Direct3D9; +using SlimDX; +using CodeImp.DoomBuilder.Geometry; +using System.Drawing.Imaging; +using CodeImp.DoomBuilder.Data; +using CodeImp.DoomBuilder.Editing; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Rendering; + +#endregion + +namespace CodeImp.DoomBuilder.VisualModes +{ + public class VisualSector : ID3DResource + { + #region ================== Constants + + #endregion + + #region ================== Variables + + // Geometry + private List fixedgeometry; + private List allgeometry; + private Dictionary> sidedefgeometry; + private VertexBuffer geobuffer; + private bool updategeo; + + // Original sector + private Sector sector; + + // Disposing + private bool isdisposed = false; + + #endregion + + #region ================== Properties + + internal List FixedGeometry { get { return fixedgeometry; } } + internal List AllGeometry { get { return allgeometry; } } + internal VertexBuffer GeometryBuffer { get { return geobuffer; } } + internal bool NeedsUpdateGeo { get { return updategeo; } set { updategeo |= value; } } + + public bool IsDisposed { get { return isdisposed; } } + public Sector Sector { get { return sector; } } + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + public VisualSector(Sector s) + { + // Initialize + this.sector = s; + allgeometry = new List(); + fixedgeometry = new List(); + sidedefgeometry = new Dictionary>(); + + // Register as resource + General.Map.Graphics.RegisterResource(this); + } + + // Disposer + public virtual void Dispose() + { + // Not already disposed? + if (!isdisposed) + { + // Clean up + if (geobuffer != null) geobuffer.Dispose(); + geobuffer = null; + + // Unregister resource + General.Map.Graphics.UnregisterResource(this); + + // Done + isdisposed = true; + } + } + + #endregion + + #region ================== Methods + + // This is called before a device is reset + // (when resized or display adapter was changed) + public virtual void UnloadResource() + { + // Trash geometry buffer + if (geobuffer != null) geobuffer.Dispose(); + geobuffer = null; + updategeo = true; + } + + // This is called resets when the device is reset + // (when resized or display adapter was changed) + public virtual void ReloadResource() + { + // Make new geometry + //Update(); + } + + // This updates the visual sector + public void Update() + { + DataStream bufferstream; + int numverts = 0; + int v = 0; + + // Trash geometry buffer + if (geobuffer != null) geobuffer.Dispose(); + geobuffer = null; + + // Count the number of vertices there are + foreach (VisualGeometry g in allgeometry) if (g.Vertices != null) numverts += g.Vertices.Length; + + // Any vertics? + if (numverts > 0) + { + // Make a new buffer + geobuffer = new VertexBuffer(General.Map.Graphics.Device, WorldVertex.Stride * numverts, + Usage.WriteOnly | Usage.Dynamic, VertexFormat.None, Pool.Default); + + // Fill the buffer + bufferstream = geobuffer.Lock(0, WorldVertex.Stride * numverts, LockFlags.Discard); + foreach (VisualGeometry g in allgeometry) + { + if ((g.Vertices != null) && (g.Vertices.Length > 0)) + { + bufferstream.WriteRange(g.Vertices); + g.VertexOffset = v; + v += g.Vertices.Length; + } + } + geobuffer.Unlock(); + bufferstream.Dispose(); + } + + // Done + updategeo = false; + } + + /// + /// This adds geometry for this sector. If the geometry inherits from VisualSidedef then it + /// will be added to the SidedefGeometry, otherwise it will be added as FixedGeometry. + /// + public void AddGeometry(VisualGeometry geo) + { + updategeo = true; + allgeometry.Add(geo); + if (geo.Sidedef != null) + { + if (!sidedefgeometry.ContainsKey(geo.Sidedef)) + sidedefgeometry[geo.Sidedef] = new List(3); + sidedefgeometry[geo.Sidedef].Add(geo); + } + else + { + fixedgeometry.Add(geo); + } + } + + /// + /// This removes all geometry. + /// + public void ClearGeometry() + { + allgeometry.Clear(); + fixedgeometry.Clear(); + sidedefgeometry.Clear(); + updategeo = true; + } + + // This gets the geometry list for the specified sidedef + public List GetSidedefGeometry(Sidedef sd) + { + if (sidedefgeometry.ContainsKey(sd)) + return sidedefgeometry[sd]; + else + return new List(); + } + + #endregion + } +} diff --git a/Source/Core/VisualModes/VisualThing.cs b/Source/Core/VisualModes/VisualThing.cs new file mode 100644 index 0000000..18acf84 --- /dev/null +++ b/Source/Core/VisualModes/VisualThing.cs @@ -0,0 +1,288 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using System.Drawing; +using System.ComponentModel; +using CodeImp.DoomBuilder.Map; +using SlimDX.Direct3D9; +using SlimDX; +using CodeImp.DoomBuilder.Geometry; +using System.Drawing.Imaging; +using CodeImp.DoomBuilder.Data; +using CodeImp.DoomBuilder.Editing; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Rendering; + +#endregion + +namespace CodeImp.DoomBuilder.VisualModes +{ + public abstract class VisualThing : IVisualPickable, ID3DResource, IComparable + { + #region ================== Constants + + #endregion + + #region ================== Variables + + // Thing + private Thing thing; + + // Texture + private ImageData texture; + + // Geometry + private WorldVertex[] vertices; + private VertexBuffer geobuffer; + private bool updategeo; + private int triangles; + + // Rendering + private int renderpass; + private Matrix orientation; + private Matrix position; + private Matrix cagescales; + private bool billboard; + private Vector2D pos2d; + private float cameradistance; + private int cagecolor; + + // Selected? + protected bool selected; + + // Disposing + private bool isdisposed = false; + + #endregion + + #region ================== Properties + + internal VertexBuffer GeometryBuffer { get { return geobuffer; } } + internal bool NeedsUpdateGeo { get { return updategeo; } } + internal int Triangles { get { return triangles; } } + internal int RenderPassInt { get { return renderpass; } } + internal Matrix Orientation { get { return orientation; } } + internal Matrix Position { get { return position; } } + internal Matrix CageScales { get { return cagescales; } } + internal int CageColor { get { return cagecolor; } } + + /// + /// Set to True to use billboarding for this thing. When using billboarding, + /// the geometry will be rotated on the XY plane to face the camera. + /// + public bool Billboard { get { return billboard; } set { billboard = value; } } + + /// + /// Returns the Thing that this VisualThing is created for. + /// + public Thing Thing { get { return thing; } } + + /// + /// Render pass in which this geometry must be rendered. Default is Solid. + /// + public RenderPass RenderPass { get { return (RenderPass)renderpass; } set { renderpass = (int)value; } } + + /// + /// Image to use as texture on the geometry. + /// + public ImageData Texture { get { return texture; } set { texture = value; } } + + /// + /// Disposed or not? + /// + public bool IsDisposed { get { return isdisposed; } } + + /// + /// Selected or not? This is only used by the core to determine what color to draw it with. + /// + public bool Selected { get { return selected; } set { selected = value; } } + + #endregion + + #region ================== Constructor / Destructor + + // Constructor + public VisualThing(Thing t) + { + // Initialize + this.thing = t; + this.renderpass = (int)RenderPass.Mask; + this.billboard = true; + this.orientation = Matrix.Identity; + this.position = Matrix.Identity; + this.cagescales = Matrix.Identity; + + // Register as resource + General.Map.Graphics.RegisterResource(this); + } + + // Disposer + public virtual void Dispose() + { + // Not already disposed? + if (!isdisposed) + { + // Clean up + if (geobuffer != null) geobuffer.Dispose(); + geobuffer = null; + + // Unregister resource + General.Map.Graphics.UnregisterResource(this); + + // Done + isdisposed = true; + } + } + + #endregion + + #region ================== Methods + + // This sets the distance from the camera + internal void CalculateCameraDistance(Vector2D campos) + { + cameradistance = Vector2D.DistanceSq(pos2d, campos); + } + + // This is called before a device is reset + // (when resized or display adapter was changed) + public void UnloadResource() + { + // Trash geometry buffer + if (geobuffer != null) geobuffer.Dispose(); + geobuffer = null; + updategeo = true; + } + + // This is called resets when the device is reset + // (when resized or display adapter was changed) + public void ReloadResource() + { + // Make new geometry + //Update(); + } + + /// + /// Sets the size of the cage around the thing geometry. + /// + public void SetCageSize(float radius, float height) + { + cagescales = Matrix.Scaling(radius, radius, height); + } + + /// + /// Sets the color of the cage around the thing geometry. + /// + public void SetCageColor(PixelColor color) + { + cagecolor = color.ToInt(); + } + + /// + /// This sets the position to use for the thing geometry. + /// + public void SetPosition(Vector3D pos) + { + pos2d = new Vector2D(pos); + position = Matrix.Translation(D3DDevice.V3(pos)); + } + + /// + /// This sets the orientation to use for the thing geometry. When using this, you may want to turn off billboarding. + /// + public void SetOrientation(Vector3D angles) + { + orientation = Matrix.RotationYawPitchRoll(angles.z, angles.y, angles.x); + } + + // This sets the vertices for the thing sprite + protected void SetVertices(ICollection verts) + { + // Copy vertices + vertices = new WorldVertex[verts.Count]; + verts.CopyTo(vertices, 0); + triangles = vertices.Length / 3; + updategeo = true; + } + + // This updates the visual thing + public virtual void Update() + { + // Do we need to update the geometry buffer? + if (updategeo) + { + // Trash geometry buffer + if (geobuffer != null) geobuffer.Dispose(); + geobuffer = null; + + // Any vertics? + if (vertices.Length > 0) + { + // Make a new buffer + geobuffer = new VertexBuffer(General.Map.Graphics.Device, WorldVertex.Stride * vertices.Length, + Usage.WriteOnly | Usage.Dynamic, VertexFormat.None, Pool.Default); + + // Fill the buffer + DataStream bufferstream = geobuffer.Lock(0, WorldVertex.Stride * vertices.Length, LockFlags.Discard); + bufferstream.WriteRange(vertices); + geobuffer.Unlock(); + bufferstream.Dispose(); + } + + // Done + updategeo = false; + } + } + + /// + /// This is called when the thing must be tested for line intersection. This should reject + /// as fast as possible to rule out all geometry that certainly does not touch the line. + /// + public virtual bool PickFastReject(Vector3D from, Vector3D to, Vector3D dir) + { + return false; + } + + /// + /// This is called when the thing must be tested for line intersection. This should perform + /// accurate hit detection and set u_ray to the position on the ray where this hits the geometry. + /// + public virtual bool PickAccurate(Vector3D from, Vector3D to, Vector3D dir, ref float u_ray) + { + return false; + } + + /// + /// This sorts things by distance from the camera. Farthest first. + /// + public int CompareTo(VisualThing other) + { + return Math.Sign(other.cameradistance - this.cameradistance); + } + + #endregion + } +} diff --git a/Source/Core/Windows/AboutForm.Designer.cs b/Source/Core/Windows/AboutForm.Designer.cs new file mode 100644 index 0000000..9717b26 --- /dev/null +++ b/Source/Core/Windows/AboutForm.Designer.cs @@ -0,0 +1,181 @@ +namespace CodeImp.DoomBuilder.Windows +{ + partial class AboutForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.Windows.Forms.Label label1; + System.Windows.Forms.PictureBox pictureBox1; + this.close = new System.Windows.Forms.Button(); + this.builderlink = new System.Windows.Forms.LinkLabel(); + this.version = new System.Windows.Forms.Label(); + this.copyversion = new System.Windows.Forms.Button(); + this.pictureBox2 = new System.Windows.Forms.PictureBox(); + this.codeimplink = new System.Windows.Forms.LinkLabel(); + label1 = new System.Windows.Forms.Label(); + pictureBox1 = new System.Windows.Forms.PictureBox(); + ((System.ComponentModel.ISupportInitialize)(pictureBox1)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox2)).BeginInit(); + this.SuspendLayout(); + // + // label1 + // + label1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + label1.FlatStyle = System.Windows.Forms.FlatStyle.System; + label1.Location = new System.Drawing.Point(15, 161); + label1.Name = "label1"; + label1.Size = new System.Drawing.Size(349, 50); + label1.TabIndex = 2; + label1.Text = "Doom Builder is designed and programmed by Pascal vd Heiden.\r\nSeveral game config" + + "urations were written by various members of the Doom community. See the website " + + "for a complete list of credits."; + // + // pictureBox1 + // + pictureBox1.Image = global::CodeImp.DoomBuilder.Properties.Resources.Splash3_small; + pictureBox1.Location = new System.Drawing.Point(10, 12); + pictureBox1.Name = "pictureBox1"; + pictureBox1.Size = new System.Drawing.Size(226, 80); + pictureBox1.SizeMode = System.Windows.Forms.PictureBoxSizeMode.AutoSize; + pictureBox1.TabIndex = 0; + pictureBox1.TabStop = false; + // + // close + // + this.close.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.close.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.close.Location = new System.Drawing.Point(265, 243); + this.close.Name = "close"; + this.close.Size = new System.Drawing.Size(116, 25); + this.close.TabIndex = 5; + this.close.Text = "Close"; + this.close.UseVisualStyleBackColor = true; + // + // builderlink + // + this.builderlink.ActiveLinkColor = System.Drawing.Color.Firebrick; + this.builderlink.AutoSize = true; + this.builderlink.LinkBehavior = System.Windows.Forms.LinkBehavior.NeverUnderline; + this.builderlink.LinkColor = System.Drawing.Color.Firebrick; + this.builderlink.Location = new System.Drawing.Point(12, 219); + this.builderlink.Name = "builderlink"; + this.builderlink.Size = new System.Drawing.Size(121, 14); + this.builderlink.TabIndex = 3; + this.builderlink.TabStop = true; + this.builderlink.Text = "www.doombuilder.com"; + this.builderlink.VisitedLinkColor = System.Drawing.Color.Firebrick; + this.builderlink.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.builderlink_LinkClicked); + // + // version + // + this.version.AutoSize = true; + this.version.FlatStyle = System.Windows.Forms.FlatStyle.System; + this.version.Location = new System.Drawing.Point(15, 118); + this.version.Name = "version"; + this.version.Size = new System.Drawing.Size(138, 14); + this.version.TabIndex = 0; + this.version.Text = "Doom Builder some version"; + // + // copyversion + // + this.copyversion.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.copyversion.Location = new System.Drawing.Point(265, 113); + this.copyversion.Name = "copyversion"; + this.copyversion.Size = new System.Drawing.Size(116, 25); + this.copyversion.TabIndex = 1; + this.copyversion.Text = "Copy Version"; + this.copyversion.UseVisualStyleBackColor = true; + this.copyversion.Click += new System.EventHandler(this.copyversion_Click); + // + // pictureBox2 + // + this.pictureBox2.Image = global::CodeImp.DoomBuilder.Properties.Resources.CLogo; + this.pictureBox2.Location = new System.Drawing.Point(293, 12); + this.pictureBox2.Name = "pictureBox2"; + this.pictureBox2.Size = new System.Drawing.Size(88, 80); + this.pictureBox2.SizeMode = System.Windows.Forms.PictureBoxSizeMode.CenterImage; + this.pictureBox2.TabIndex = 10; + this.pictureBox2.TabStop = false; + // + // codeimplink + // + this.codeimplink.ActiveLinkColor = System.Drawing.Color.Firebrick; + this.codeimplink.AutoSize = true; + this.codeimplink.LinkBehavior = System.Windows.Forms.LinkBehavior.NeverUnderline; + this.codeimplink.LinkColor = System.Drawing.Color.Firebrick; + this.codeimplink.Location = new System.Drawing.Point(12, 239); + this.codeimplink.Name = "codeimplink"; + this.codeimplink.Size = new System.Drawing.Size(103, 14); + this.codeimplink.TabIndex = 4; + this.codeimplink.TabStop = true; + this.codeimplink.Text = "www.codeimp.com"; + this.codeimplink.VisitedLinkColor = System.Drawing.Color.Firebrick; + this.codeimplink.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.codeimplink_LinkClicked); + // + // AboutForm + // + this.AcceptButton = this.close; + this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; + this.CancelButton = this.close; + this.ClientSize = new System.Drawing.Size(393, 280); + this.Controls.Add(this.codeimplink); + this.Controls.Add(this.pictureBox2); + this.Controls.Add(this.copyversion); + this.Controls.Add(this.version); + this.Controls.Add(this.builderlink); + this.Controls.Add(this.close); + this.Controls.Add(label1); + this.Controls.Add(pictureBox1); + this.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "AboutForm"; + this.Opacity = 0; + this.ShowIcon = false; + this.ShowInTaskbar = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "About Doom Builder"; + ((System.ComponentModel.ISupportInitialize)(pictureBox1)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox2)).EndInit(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Button close; + private System.Windows.Forms.LinkLabel builderlink; + private System.Windows.Forms.Label version; + private System.Windows.Forms.Button copyversion; + private System.Windows.Forms.PictureBox pictureBox2; + private System.Windows.Forms.LinkLabel codeimplink; + } +} \ No newline at end of file diff --git a/Source/Core/Windows/AboutForm.cs b/Source/Core/Windows/AboutForm.cs new file mode 100644 index 0000000..4f8c08f --- /dev/null +++ b/Source/Core/Windows/AboutForm.cs @@ -0,0 +1,67 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Text; +using System.Windows.Forms; +using Microsoft.Win32; +using System.Diagnostics; +using System.Reflection; +using CodeImp.DoomBuilder.Controls; + +#endregion + +namespace CodeImp.DoomBuilder.Windows +{ + internal partial class AboutForm : DelayedForm + { + // Constructor + public AboutForm() + { + // Initialize + InitializeComponent(); + + // Show version + string postfix = ""; + if (General.DebugBuild) postfix = "(debug)"; + version.Text = Application.ProductName + " version " + Application.ProductVersion + " " + postfix; + } + + // Launch Doom Builder website + private void builderlink_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) + { + General.OpenWebsite("http://" + builderlink.Text); + } + + // Launch CodeImp website + private void codeimplink_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) + { + General.OpenWebsite("http://" + codeimplink.Text); + } + + // This copies the version number to clipboard + private void copyversion_Click(object sender, EventArgs e) + { + Clipboard.Clear(); + Clipboard.SetText(Application.ProductVersion); + } + } +} \ No newline at end of file diff --git a/Source/Core/Windows/AboutForm.resx b/Source/Core/Windows/AboutForm.resx new file mode 100644 index 0000000..f3d2a1b --- /dev/null +++ b/Source/Core/Windows/AboutForm.resx @@ -0,0 +1,153 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + True + + + False + + + True + + + False + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + \ No newline at end of file diff --git a/Source/Core/Windows/ActionBrowserForm.Designer.cs b/Source/Core/Windows/ActionBrowserForm.Designer.cs new file mode 100644 index 0000000..713c1ea --- /dev/null +++ b/Source/Core/Windows/ActionBrowserForm.Designer.cs @@ -0,0 +1,505 @@ +namespace CodeImp.DoomBuilder.Windows +{ + partial class ActionBrowserForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.Windows.Forms.Label label1; + System.Windows.Forms.Label label2; + System.Windows.Forms.Label label3; + System.Windows.Forms.Label label4; + System.Windows.Forms.Label label5; + System.Windows.Forms.Label label6; + System.Windows.Forms.GroupBox groupBox1; + System.Windows.Forms.Label label7; + System.Windows.Forms.GroupBox groupBox2; + this.category = new System.Windows.Forms.ComboBox(); + this.option7 = new System.Windows.Forms.ComboBox(); + this.option7label = new System.Windows.Forms.Label(); + this.option6 = new System.Windows.Forms.ComboBox(); + this.option6label = new System.Windows.Forms.Label(); + this.option5 = new System.Windows.Forms.ComboBox(); + this.option5label = new System.Windows.Forms.Label(); + this.option4 = new System.Windows.Forms.ComboBox(); + this.option4label = new System.Windows.Forms.Label(); + this.option3 = new System.Windows.Forms.ComboBox(); + this.option3label = new System.Windows.Forms.Label(); + this.option2 = new System.Windows.Forms.ComboBox(); + this.option2label = new System.Windows.Forms.Label(); + this.option1 = new System.Windows.Forms.ComboBox(); + this.option1label = new System.Windows.Forms.Label(); + this.option0 = new System.Windows.Forms.ComboBox(); + this.option0label = new System.Windows.Forms.Label(); + this.cancel = new System.Windows.Forms.Button(); + this.apply = new System.Windows.Forms.Button(); + this.actions = new System.Windows.Forms.TreeView(); + this.tabs = new System.Windows.Forms.TabControl(); + this.tabactions = new System.Windows.Forms.TabPage(); + this.prefixespanel = new System.Windows.Forms.Panel(); + this.tabgeneralized = new System.Windows.Forms.TabPage(); + label1 = new System.Windows.Forms.Label(); + label2 = new System.Windows.Forms.Label(); + label3 = new System.Windows.Forms.Label(); + label4 = new System.Windows.Forms.Label(); + label5 = new System.Windows.Forms.Label(); + label6 = new System.Windows.Forms.Label(); + groupBox1 = new System.Windows.Forms.GroupBox(); + label7 = new System.Windows.Forms.Label(); + groupBox2 = new System.Windows.Forms.GroupBox(); + groupBox1.SuspendLayout(); + groupBox2.SuspendLayout(); + this.tabs.SuspendLayout(); + this.tabactions.SuspendLayout(); + this.prefixespanel.SuspendLayout(); + this.tabgeneralized.SuspendLayout(); + this.SuspendLayout(); + // + // label1 + // + label1.AutoSize = true; + label1.Location = new System.Drawing.Point(27, 0); + label1.Name = "label1"; + label1.Size = new System.Drawing.Size(60, 14); + label1.TabIndex = 21; + label1.Text = "S = Switch"; + // + // label2 + // + label2.AutoSize = true; + label2.Location = new System.Drawing.Point(140, 0); + label2.Name = "label2"; + label2.Size = new System.Drawing.Size(77, 14); + label2.TabIndex = 22; + label2.Text = "W = Walk over"; + // + // label3 + // + label3.AutoSize = true; + label3.Location = new System.Drawing.Point(269, 0); + label3.Name = "label3"; + label3.Size = new System.Drawing.Size(63, 14); + label3.TabIndex = 23; + label3.Text = "G = Gunfire"; + // + // label4 + // + label4.AutoSize = true; + label4.Location = new System.Drawing.Point(27, 16); + label4.Name = "label4"; + label4.Size = new System.Drawing.Size(49, 14); + label4.TabIndex = 24; + label4.Text = "D = Door"; + // + // label5 + // + label5.AutoSize = true; + label5.Location = new System.Drawing.Point(140, 16); + label5.Name = "label5"; + label5.Size = new System.Drawing.Size(80, 14); + label5.TabIndex = 25; + label5.Text = "R = Repeatable"; + // + // label6 + // + label6.AutoSize = true; + label6.Location = new System.Drawing.Point(269, 16); + label6.Name = "label6"; + label6.Size = new System.Drawing.Size(74, 14); + label6.TabIndex = 26; + label6.Text = "1 = Once only"; + // + // groupBox1 + // + groupBox1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + groupBox1.Controls.Add(this.category); + groupBox1.Controls.Add(label7); + groupBox1.Location = new System.Drawing.Point(6, 6); + groupBox1.Name = "groupBox1"; + groupBox1.Size = new System.Drawing.Size(379, 65); + groupBox1.TabIndex = 0; + groupBox1.TabStop = false; + groupBox1.Text = " Category "; + // + // category + // + this.category.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.category.FormattingEnabled = true; + this.category.Location = new System.Drawing.Point(118, 25); + this.category.Name = "category"; + this.category.Size = new System.Drawing.Size(199, 22); + this.category.TabIndex = 0; + this.category.SelectedIndexChanged += new System.EventHandler(this.category_SelectedIndexChanged); + // + // label7 + // + label7.AutoSize = true; + label7.Location = new System.Drawing.Point(58, 28); + label7.Name = "label7"; + label7.Size = new System.Drawing.Size(54, 14); + label7.TabIndex = 0; + label7.Text = "Category:"; + // + // groupBox2 + // + groupBox2.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + groupBox2.Controls.Add(this.option7); + groupBox2.Controls.Add(this.option7label); + groupBox2.Controls.Add(this.option6); + groupBox2.Controls.Add(this.option6label); + groupBox2.Controls.Add(this.option5); + groupBox2.Controls.Add(this.option5label); + groupBox2.Controls.Add(this.option4); + groupBox2.Controls.Add(this.option4label); + groupBox2.Controls.Add(this.option3); + groupBox2.Controls.Add(this.option3label); + groupBox2.Controls.Add(this.option2); + groupBox2.Controls.Add(this.option2label); + groupBox2.Controls.Add(this.option1); + groupBox2.Controls.Add(this.option1label); + groupBox2.Controls.Add(this.option0); + groupBox2.Controls.Add(this.option0label); + groupBox2.Location = new System.Drawing.Point(6, 77); + groupBox2.Name = "groupBox2"; + groupBox2.Size = new System.Drawing.Size(379, 326); + groupBox2.TabIndex = 1; + groupBox2.TabStop = false; + groupBox2.Text = " Options "; + // + // option7 + // + this.option7.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.option7.FormattingEnabled = true; + this.option7.Location = new System.Drawing.Point(118, 280); + this.option7.Name = "option7"; + this.option7.Size = new System.Drawing.Size(199, 22); + this.option7.TabIndex = 7; + this.option7.Visible = false; + // + // option7label + // + this.option7label.Location = new System.Drawing.Point(3, 283); + this.option7label.Name = "option7label"; + this.option7label.Size = new System.Drawing.Size(109, 19); + this.option7label.TabIndex = 16; + this.option7label.Text = "Option:"; + this.option7label.TextAlign = System.Drawing.ContentAlignment.TopRight; + this.option7label.Visible = false; + // + // option6 + // + this.option6.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.option6.FormattingEnabled = true; + this.option6.Location = new System.Drawing.Point(118, 244); + this.option6.Name = "option6"; + this.option6.Size = new System.Drawing.Size(199, 22); + this.option6.TabIndex = 6; + this.option6.Visible = false; + // + // option6label + // + this.option6label.Location = new System.Drawing.Point(3, 247); + this.option6label.Name = "option6label"; + this.option6label.Size = new System.Drawing.Size(109, 19); + this.option6label.TabIndex = 14; + this.option6label.Text = "Option:"; + this.option6label.TextAlign = System.Drawing.ContentAlignment.TopRight; + this.option6label.Visible = false; + // + // option5 + // + this.option5.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.option5.FormattingEnabled = true; + this.option5.Location = new System.Drawing.Point(118, 208); + this.option5.Name = "option5"; + this.option5.Size = new System.Drawing.Size(199, 22); + this.option5.TabIndex = 5; + this.option5.Visible = false; + // + // option5label + // + this.option5label.Location = new System.Drawing.Point(3, 211); + this.option5label.Name = "option5label"; + this.option5label.Size = new System.Drawing.Size(109, 19); + this.option5label.TabIndex = 12; + this.option5label.Text = "Option:"; + this.option5label.TextAlign = System.Drawing.ContentAlignment.TopRight; + this.option5label.Visible = false; + // + // option4 + // + this.option4.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.option4.FormattingEnabled = true; + this.option4.Location = new System.Drawing.Point(118, 172); + this.option4.Name = "option4"; + this.option4.Size = new System.Drawing.Size(199, 22); + this.option4.TabIndex = 4; + this.option4.Visible = false; + // + // option4label + // + this.option4label.Location = new System.Drawing.Point(3, 175); + this.option4label.Name = "option4label"; + this.option4label.Size = new System.Drawing.Size(109, 19); + this.option4label.TabIndex = 10; + this.option4label.Text = "Option:"; + this.option4label.TextAlign = System.Drawing.ContentAlignment.TopRight; + this.option4label.Visible = false; + // + // option3 + // + this.option3.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.option3.FormattingEnabled = true; + this.option3.Location = new System.Drawing.Point(118, 136); + this.option3.Name = "option3"; + this.option3.Size = new System.Drawing.Size(199, 22); + this.option3.TabIndex = 3; + this.option3.Visible = false; + // + // option3label + // + this.option3label.Location = new System.Drawing.Point(3, 139); + this.option3label.Name = "option3label"; + this.option3label.Size = new System.Drawing.Size(109, 19); + this.option3label.TabIndex = 8; + this.option3label.Text = "Option:"; + this.option3label.TextAlign = System.Drawing.ContentAlignment.TopRight; + this.option3label.Visible = false; + // + // option2 + // + this.option2.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.option2.FormattingEnabled = true; + this.option2.Location = new System.Drawing.Point(118, 100); + this.option2.Name = "option2"; + this.option2.Size = new System.Drawing.Size(199, 22); + this.option2.TabIndex = 2; + this.option2.Visible = false; + // + // option2label + // + this.option2label.Location = new System.Drawing.Point(3, 103); + this.option2label.Name = "option2label"; + this.option2label.Size = new System.Drawing.Size(109, 19); + this.option2label.TabIndex = 6; + this.option2label.Text = "Option:"; + this.option2label.TextAlign = System.Drawing.ContentAlignment.TopRight; + this.option2label.Visible = false; + // + // option1 + // + this.option1.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.option1.FormattingEnabled = true; + this.option1.Location = new System.Drawing.Point(118, 64); + this.option1.Name = "option1"; + this.option1.Size = new System.Drawing.Size(199, 22); + this.option1.TabIndex = 1; + this.option1.Visible = false; + // + // option1label + // + this.option1label.Location = new System.Drawing.Point(3, 67); + this.option1label.Name = "option1label"; + this.option1label.Size = new System.Drawing.Size(109, 19); + this.option1label.TabIndex = 4; + this.option1label.Text = "Option:"; + this.option1label.TextAlign = System.Drawing.ContentAlignment.TopRight; + this.option1label.Visible = false; + // + // option0 + // + this.option0.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.option0.FormattingEnabled = true; + this.option0.Location = new System.Drawing.Point(118, 28); + this.option0.Name = "option0"; + this.option0.Size = new System.Drawing.Size(199, 22); + this.option0.TabIndex = 0; + this.option0.Visible = false; + // + // option0label + // + this.option0label.Location = new System.Drawing.Point(3, 31); + this.option0label.Name = "option0label"; + this.option0label.Size = new System.Drawing.Size(109, 19); + this.option0label.TabIndex = 2; + this.option0label.Text = "Option:"; + this.option0label.TextAlign = System.Drawing.ContentAlignment.TopRight; + this.option0label.Visible = false; + // + // cancel + // + this.cancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.cancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.cancel.Location = new System.Drawing.Point(297, 459); + this.cancel.Name = "cancel"; + this.cancel.Size = new System.Drawing.Size(112, 27); + this.cancel.TabIndex = 2; + this.cancel.Text = "Cancel"; + this.cancel.UseVisualStyleBackColor = true; + this.cancel.Click += new System.EventHandler(this.cancel_Click); + // + // apply + // + this.apply.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.apply.Location = new System.Drawing.Point(177, 459); + this.apply.Name = "apply"; + this.apply.Size = new System.Drawing.Size(112, 27); + this.apply.TabIndex = 1; + this.apply.Text = "OK"; + this.apply.UseVisualStyleBackColor = true; + this.apply.Click += new System.EventHandler(this.apply_Click); + // + // actions + // + this.actions.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.actions.HideSelection = false; + this.actions.Location = new System.Drawing.Point(6, 52); + this.actions.Name = "actions"; + this.actions.Size = new System.Drawing.Size(379, 351); + this.actions.TabIndex = 0; + this.actions.DoubleClick += new System.EventHandler(this.actions_DoubleClick); + // + // tabs + // + this.tabs.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.tabs.Controls.Add(this.tabactions); + this.tabs.Controls.Add(this.tabgeneralized); + this.tabs.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tabs.ItemSize = new System.Drawing.Size(150, 19); + this.tabs.Location = new System.Drawing.Point(10, 10); + this.tabs.Margin = new System.Windows.Forms.Padding(1); + this.tabs.Name = "tabs"; + this.tabs.SelectedIndex = 0; + this.tabs.Size = new System.Drawing.Size(399, 436); + this.tabs.SizeMode = System.Windows.Forms.TabSizeMode.Fixed; + this.tabs.TabIndex = 0; + // + // tabactions + // + this.tabactions.Controls.Add(this.actions); + this.tabactions.Controls.Add(this.prefixespanel); + this.tabactions.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tabactions.Location = new System.Drawing.Point(4, 23); + this.tabactions.Name = "tabactions"; + this.tabactions.Padding = new System.Windows.Forms.Padding(3); + this.tabactions.Size = new System.Drawing.Size(391, 409); + this.tabactions.TabIndex = 0; + this.tabactions.Text = "Predefined Actions"; + this.tabactions.UseVisualStyleBackColor = true; + // + // prefixespanel + // + this.prefixespanel.Controls.Add(label6); + this.prefixespanel.Controls.Add(label5); + this.prefixespanel.Controls.Add(label4); + this.prefixespanel.Controls.Add(label3); + this.prefixespanel.Controls.Add(label2); + this.prefixespanel.Controls.Add(label1); + this.prefixespanel.Location = new System.Drawing.Point(7, 12); + this.prefixespanel.Name = "prefixespanel"; + this.prefixespanel.Size = new System.Drawing.Size(378, 34); + this.prefixespanel.TabIndex = 27; + // + // tabgeneralized + // + this.tabgeneralized.Controls.Add(groupBox2); + this.tabgeneralized.Controls.Add(groupBox1); + this.tabgeneralized.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tabgeneralized.Location = new System.Drawing.Point(4, 23); + this.tabgeneralized.Name = "tabgeneralized"; + this.tabgeneralized.Padding = new System.Windows.Forms.Padding(3); + this.tabgeneralized.Size = new System.Drawing.Size(391, 409); + this.tabgeneralized.TabIndex = 1; + this.tabgeneralized.Text = "Generalized Actions"; + this.tabgeneralized.UseVisualStyleBackColor = true; + // + // ActionBrowserForm + // + this.AcceptButton = this.apply; + this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; + this.CancelButton = this.cancel; + this.ClientSize = new System.Drawing.Size(419, 496); + this.Controls.Add(this.tabs); + this.Controls.Add(this.cancel); + this.Controls.Add(this.apply); + this.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "ActionBrowserForm"; + this.Opacity = 0; + this.ShowInTaskbar = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Edit Action"; + this.Load += new System.EventHandler(this.ActionBrowserForm_Load); + groupBox1.ResumeLayout(false); + groupBox1.PerformLayout(); + groupBox2.ResumeLayout(false); + this.tabs.ResumeLayout(false); + this.tabactions.ResumeLayout(false); + this.prefixespanel.ResumeLayout(false); + this.prefixespanel.PerformLayout(); + this.tabgeneralized.ResumeLayout(false); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.Button cancel; + private System.Windows.Forms.Button apply; + private System.Windows.Forms.TreeView actions; + private System.Windows.Forms.TabControl tabs; + private System.Windows.Forms.TabPage tabactions; + private System.Windows.Forms.TabPage tabgeneralized; + private System.Windows.Forms.ComboBox category; + private System.Windows.Forms.ComboBox option7; + private System.Windows.Forms.Label option7label; + private System.Windows.Forms.ComboBox option6; + private System.Windows.Forms.Label option6label; + private System.Windows.Forms.ComboBox option5; + private System.Windows.Forms.Label option5label; + private System.Windows.Forms.ComboBox option4; + private System.Windows.Forms.Label option4label; + private System.Windows.Forms.ComboBox option3; + private System.Windows.Forms.Label option3label; + private System.Windows.Forms.ComboBox option2; + private System.Windows.Forms.Label option2label; + private System.Windows.Forms.ComboBox option1; + private System.Windows.Forms.Label option1label; + private System.Windows.Forms.ComboBox option0; + private System.Windows.Forms.Label option0label; + private System.Windows.Forms.Panel prefixespanel; + } +} diff --git a/Source/Core/Windows/ActionBrowserForm.cs b/Source/Core/Windows/ActionBrowserForm.cs new file mode 100644 index 0000000..033fefe --- /dev/null +++ b/Source/Core/Windows/ActionBrowserForm.cs @@ -0,0 +1,288 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Text; +using System.Windows.Forms; +using Microsoft.Win32; +using System.Diagnostics; +using CodeImp.DoomBuilder.Actions; +using CodeImp.DoomBuilder.Data; +using CodeImp.DoomBuilder.Config; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Controls; + +#endregion + +namespace CodeImp.DoomBuilder.Windows +{ + internal partial class ActionBrowserForm : DelayedForm + { + // Constants + private const int MAX_OPTIONS = 8; + + // Variables + private int selectedaction; + private ComboBox[] options; + private Label[] optionlbls; + + // Properties + public int SelectedAction { get { return selectedaction; } } + + // Constructor + public ActionBrowserForm(int action) + { + TreeNode cn, n; + GeneralizedCategory sc; + int actionbits; + + // Initialize + InitializeComponent(); + + // Make array references for controls + options = new ComboBox[] { option0, option1, option2, option3, option4, option5, option6, option7 }; + optionlbls = new Label[] { option0label, option1label, option2label, option3label, option4label, + option5label, option6label, option7label }; + + // Show prefixes panel only for doom type maps + if (General.Map.FormatInterface.HasBuiltInActivations) + { + prefixespanel.Visible = false; + actions.Height += actions.Top - prefixespanel.Top; + actions.Top = prefixespanel.Top; + } + + // Go for all predefined categories + foreach (LinedefActionCategory ac in General.Map.Config.ActionCategories) + { + // Empty category names will not be created + // (those actions will go in the root of the tree) + if (ac.Title.Length > 0) + { + // Create category + cn = actions.Nodes.Add(ac.Title); + foreach (LinedefActionInfo ai in ac.Actions) + { + // Create action + n = cn.Nodes.Add(ai.Title); + n.Tag = ai; + + // This is the given action? + if (ai.Index == action) + { + // Select this and expand the category + cn.Expand(); + actions.SelectedNode = n; + n.EnsureVisible(); + } + } + } + else + { + // Put actions in the tree root + foreach (LinedefActionInfo ai in ac.Actions) + { + // Create action + n = actions.Nodes.Add(ai.Title); + n.Tag = ai; + } + } + } + + // Using generalized actions? + if (General.Map.Config.GeneralizedActions) + { + // Add for all generalized categories to the combobox + category.Items.AddRange(General.Map.Config.GenActionCategories.ToArray()); + + // Given action is generalized? + if (GameConfiguration.IsGeneralized(action, General.Map.Config.GenActionCategories)) + { + // Open the generalized tab + tabs.SelectedTab = tabgeneralized; + + // Select category + foreach (GeneralizedCategory ac in category.Items) + if ((action >= ac.Offset) && (action < (ac.Offset + ac.Length))) category.SelectedItem = ac; + + // Anything selected? + if (category.SelectedIndex > -1) + { + // Go for all options in selected category + sc = category.SelectedItem as GeneralizedCategory; + actionbits = action - sc.Offset; + for (int i = 0; i < MAX_OPTIONS; i++) + { + // Option used? + if (i < sc.Options.Count) + { + // Go for all bits + foreach (GeneralizedBit ab in sc.Options[i].Bits) + { + // Select this setting if matches + if ((actionbits & ab.Index) == ab.Index) options[i].SelectedItem = ab; + } + } + } + } + } + } + else + { + // Remove generalized tab + tabs.TabPages.Remove(tabgeneralized); + } + } + + // This browses for an action + // Returns the new action or the same action when cancelled + public static int BrowseAction(IWin32Window owner, int action) + { + ActionBrowserForm f = new ActionBrowserForm(action); + if (f.ShowDialog(owner) == DialogResult.OK) + action = f.SelectedAction; + else + action = 0; // villsa + f.Dispose(); + return action; + } + + // OK clicked + private void apply_Click(object sender, EventArgs e) + { + GeneralizedCategory sc; + + // Presume no result + selectedaction = 0; + + // Predefined action? + if (tabs.SelectedTab == tabactions) + { + // Action node selected? + if ((actions.SelectedNode != null) && (actions.SelectedNode.Tag is LinedefActionInfo)) + { + // Our result + selectedaction = (actions.SelectedNode.Tag as LinedefActionInfo).Index; + } + } + // Generalized action + else + { + // Category selected? + if (category.SelectedIndex > -1) + { + // Add category bits and go for all options + sc = category.SelectedItem as GeneralizedCategory; + selectedaction = sc.Offset; + for (int i = 0; i < MAX_OPTIONS; i++) + { + // Option used? + if (i < sc.Options.Count) + { + // Add selected bits + if (options[i].SelectedIndex > -1) + selectedaction += (options[i].SelectedItem as GeneralizedBit).Index; + } + } + } + } + + // Done + this.DialogResult = DialogResult.OK; + this.Close(); + } + + // Cancel clicked + private void cancel_Click(object sender, EventArgs e) + { + // Leave + this.DialogResult = DialogResult.Cancel; + this.Close(); + } + + // Generalized category selected + private void category_SelectedIndexChanged(object sender, EventArgs e) + { + GeneralizedCategory ac; + + // Category selected? + if (category.SelectedIndex > -1) + { + // Get the category + ac = category.SelectedItem as GeneralizedCategory; + + // Go for all options + for (int i = 0; i < MAX_OPTIONS; i++) + { + // Option used in selected category? + if (i < ac.Options.Count) + { + // Setup controls + optionlbls[i].Text = ac.Options[i].Name + ":"; + options[i].Items.Clear(); + options[i].Items.AddRange(ac.Options[i].Bits.ToArray()); + + // Show option + options[i].Visible = true; + optionlbls[i].Visible = true; + } + else + { + // Hide option + options[i].Visible = false; + optionlbls[i].Visible = false; + } + } + } + else + { + // Hide all options + for (int i = 0; i < MAX_OPTIONS; i++) + { + options[i].Visible = false; + optionlbls[i].Visible = false; + } + } + } + + // Double clicking on item + private void actions_DoubleClick(object sender, EventArgs e) + { + // Action node selected? + if ((actions.SelectedNode != null) && (actions.SelectedNode.Tag is LinedefActionInfo)) + { + if (apply.Enabled) apply_Click(this, EventArgs.Empty); + } + } + + private void ActionBrowserForm_Load(object sender, EventArgs e) // villsa + { + if (General.Map.FormatInterface.InDoom64Mode) + this.prefixespanel.Hide(); + } + + private void checkBox6_CheckedChanged(object sender, EventArgs e) + { + + } + } +} diff --git a/Source/Core/Windows/ActionBrowserForm.resx b/Source/Core/Windows/ActionBrowserForm.resx new file mode 100644 index 0000000..b69d057 --- /dev/null +++ b/Source/Core/Windows/ActionBrowserForm.resx @@ -0,0 +1,306 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + True + + + False + + + True + + + False + + + True + + + False + + + True + + + False + + + True + + + False + + + True + + + False + + + True + + + False + + + True + + + True + + + False + + + True + + + True + + + False + + + True + + + False + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + diff --git a/Source/Core/Windows/AngleForm.Designer.cs b/Source/Core/Windows/AngleForm.Designer.cs new file mode 100644 index 0000000..f9e615b --- /dev/null +++ b/Source/Core/Windows/AngleForm.Designer.cs @@ -0,0 +1,99 @@ +namespace CodeImp.DoomBuilder.Windows +{ + partial class AngleForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.angle = new CodeImp.DoomBuilder.Controls.AngleControl(); + this.cancel = new System.Windows.Forms.Button(); + this.apply = new System.Windows.Forms.Button(); + this.SuspendLayout(); + // + // angle + // + this.angle.BackColor = System.Drawing.SystemColors.Control; + this.angle.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.angle.Location = new System.Drawing.Point(60, 22); + this.angle.Name = "angle"; + this.angle.Size = new System.Drawing.Size(84, 84); + this.angle.TabIndex = 0; + this.angle.Value = 0; + // + // cancel + // + this.cancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.cancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.cancel.Location = new System.Drawing.Point(105, 131); + this.cancel.Name = "cancel"; + this.cancel.Size = new System.Drawing.Size(91, 25); + this.cancel.TabIndex = 2; + this.cancel.Text = "Cancel"; + this.cancel.UseVisualStyleBackColor = true; + this.cancel.Click += new System.EventHandler(this.cancel_Click); + // + // apply + // + this.apply.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.apply.Location = new System.Drawing.Point(8, 131); + this.apply.Name = "apply"; + this.apply.Size = new System.Drawing.Size(91, 25); + this.apply.TabIndex = 1; + this.apply.Text = "OK"; + this.apply.UseVisualStyleBackColor = true; + this.apply.Click += new System.EventHandler(this.apply_Click); + // + // AngleForm + // + this.AcceptButton = this.apply; + this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; + this.CancelButton = this.cancel; + this.ClientSize = new System.Drawing.Size(204, 165); + this.Controls.Add(this.cancel); + this.Controls.Add(this.apply); + this.Controls.Add(this.angle); + this.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "AngleForm"; + this.Opacity = 0; + this.ShowIcon = false; + this.ShowInTaskbar = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Angle"; + this.ResumeLayout(false); + + } + + #endregion + + private CodeImp.DoomBuilder.Controls.AngleControl angle; + private System.Windows.Forms.Button cancel; + private System.Windows.Forms.Button apply; + } +} \ No newline at end of file diff --git a/Source/Core/Windows/AngleForm.cs b/Source/Core/Windows/AngleForm.cs new file mode 100644 index 0000000..e10eebf --- /dev/null +++ b/Source/Core/Windows/AngleForm.cs @@ -0,0 +1,109 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Text; +using System.Windows.Forms; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Data; +using CodeImp.DoomBuilder.IO; +using System.IO; +using CodeImp.DoomBuilder.Config; +using CodeImp.DoomBuilder.Editing; +using CodeImp.DoomBuilder.Controls; + +#endregion + +namespace CodeImp.DoomBuilder.Windows +{ + public partial class AngleForm : DelayedForm + { + #region ================== Variables + + private bool setup; + private int value; + + #endregion + + #region ================== Properties + + public int Value { get { return value; } } + + #endregion + + #region ================== Constructor + + // Constructor + public AngleForm() + { + InitializeComponent(); + } + + #endregion + + #region ================== Events + + // Cancel clicked + private void cancel_Click(object sender, EventArgs e) + { + // Close + DialogResult = DialogResult.Cancel; + this.Close(); + } + + // OK clicked + private void apply_Click(object sender, EventArgs e) + { + this.value = angle.Value; + + // Done + DialogResult = DialogResult.OK; + this.Close(); + } + + #endregion + + #region ================== Methods + + // Setup from EnumList + public void Setup(int value) + { + setup = true; + this.value = value; + angle.Value = value; + setup = false; + } + + // This shows the dialog + // Returns the flags or the same flags when cancelled + public static int ShowDialog(IWin32Window owner, int value) + { + int result = value; + AngleForm f = new AngleForm(); + f.Setup(value); + if (f.ShowDialog(owner) == DialogResult.OK) result = f.Value; + f.Dispose(); + return result; + } + + #endregion + } +} \ No newline at end of file diff --git a/Source/Core/Windows/AngleForm.resx b/Source/Core/Windows/AngleForm.resx new file mode 100644 index 0000000..20ce326 --- /dev/null +++ b/Source/Core/Windows/AngleForm.resx @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + True + + + True + + + True + + + True + + \ No newline at end of file diff --git a/Source/Core/Windows/BitFlagsForm.Designer.cs b/Source/Core/Windows/BitFlagsForm.Designer.cs new file mode 100644 index 0000000..9fd3248 --- /dev/null +++ b/Source/Core/Windows/BitFlagsForm.Designer.cs @@ -0,0 +1,101 @@ +namespace CodeImp.DoomBuilder.Windows +{ + partial class BitFlagsForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.options = new CodeImp.DoomBuilder.Controls.CheckboxArrayControl(); + this.cancel = new System.Windows.Forms.Button(); + this.apply = new System.Windows.Forms.Button(); + this.SuspendLayout(); + // + // options + // + this.options.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.options.AutoScroll = true; + this.options.Columns = 1; + this.options.Location = new System.Drawing.Point(25, 21); + this.options.Name = "options"; + this.options.Size = new System.Drawing.Size(164, 158); + this.options.TabIndex = 0; + // + // cancel + // + this.cancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.cancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.cancel.Location = new System.Drawing.Point(106, 198); + this.cancel.Name = "cancel"; + this.cancel.Size = new System.Drawing.Size(91, 25); + this.cancel.TabIndex = 2; + this.cancel.Text = "Cancel"; + this.cancel.UseVisualStyleBackColor = true; + this.cancel.Click += new System.EventHandler(this.cancel_Click); + // + // apply + // + this.apply.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.apply.Location = new System.Drawing.Point(9, 198); + this.apply.Name = "apply"; + this.apply.Size = new System.Drawing.Size(91, 25); + this.apply.TabIndex = 1; + this.apply.Text = "OK"; + this.apply.UseVisualStyleBackColor = true; + this.apply.Click += new System.EventHandler(this.apply_Click); + // + // BitFlagsForm + // + this.AcceptButton = this.apply; + this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; + this.CancelButton = this.cancel; + this.ClientSize = new System.Drawing.Size(206, 232); + this.Controls.Add(this.cancel); + this.Controls.Add(this.apply); + this.Controls.Add(this.options); + this.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "BitFlagsForm"; + this.Opacity = 0; + this.ShowIcon = false; + this.ShowInTaskbar = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Options"; + this.ResumeLayout(false); + + } + + #endregion + + private CodeImp.DoomBuilder.Controls.CheckboxArrayControl options; + private System.Windows.Forms.Button cancel; + private System.Windows.Forms.Button apply; + } +} \ No newline at end of file diff --git a/Source/Core/Windows/BitFlagsForm.cs b/Source/Core/Windows/BitFlagsForm.cs new file mode 100644 index 0000000..383d6db --- /dev/null +++ b/Source/Core/Windows/BitFlagsForm.cs @@ -0,0 +1,180 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Text; +using System.Windows.Forms; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Data; +using CodeImp.DoomBuilder.IO; +using System.IO; +using CodeImp.DoomBuilder.Config; +using CodeImp.DoomBuilder.Editing; +using CodeImp.DoomBuilder.Controls; + +#endregion + +namespace CodeImp.DoomBuilder.Windows +{ + public partial class BitFlagsForm : DelayedForm + { + #region ================== Variables + + private bool setup; + private int value; + + #endregion + + #region ================== Properties + + public int Value { get { return value; } } + + #endregion + + #region ================== Constructor + + // Constructor + public BitFlagsForm() + { + // Initialize + InitializeComponent(); + } + + #endregion + + #region ================== Events + + // When a checkbox is clicked + private void box_CheckedChanged(object sender, EventArgs e) + { + if (!setup) + { + // Now setting up + setup = true; + + // Get this checkbox + CheckBox thisbox = (sender as CheckBox); + + // Checking or unchecking? + if (thisbox.Checked) + { + // Go for all other options + foreach (CheckBox b in options.Checkboxes) + { + // Not the same box? + if (b != sender) + { + // Overlapping bit flags? + if (((int)b.Tag & (int)thisbox.Tag) != 0) + { + // Uncheck the other + b.Checked = false; + } + } + } + } + + // Done + setup = false; + } + } + + // Cancel clicked + private void cancel_Click(object sender, EventArgs e) + { + // Close + DialogResult = DialogResult.Cancel; + this.Close(); + } + + // OK clicked + private void apply_Click(object sender, EventArgs e) + { + this.value = 0; + + // Go for all checkboxes to make the final value + foreach (CheckBox b in options.Checkboxes) + if (b.Checked) value |= (int)b.Tag; + + // Done + DialogResult = DialogResult.OK; + this.Close(); + } + + #endregion + + #region ================== Methods + + // Setup from EnumList + public void Setup(EnumList flags, int value) + { + setup = true; + this.value = value; + + // Make a checkbox for each item + foreach (EnumItem item in flags) + { + // Make the checkbox + CheckBox box = options.Add(item.Title, item.GetIntValue()); + + // Bind checking event + box.CheckedChanged += new EventHandler(box_CheckedChanged); + + // Checking the box? + if ((value & (int)box.Tag) == (int)box.Tag) + { + box.Checked = true; + + // Go for all other checkboxes + foreach (CheckBox b in options.Checkboxes) + { + // Not the same box? + if (b != box) + { + // Overlapping bit flags? + if (((int)b.Tag & (int)box.Tag) != 0) + { + // Uncheck the other + b.Checked = false; + } + } + } + } + } + + setup = false; + } + + // This shows the dialog + // Returns the flags or the same flags when cancelled + public static int ShowDialog(IWin32Window owner, EnumList flags, int value) + { + int result = value; + BitFlagsForm f = new BitFlagsForm(); + f.Setup(flags, value); + if (f.ShowDialog(owner) == DialogResult.OK) result = f.Value; + f.Dispose(); + return result; + } + + #endregion + } +} \ No newline at end of file diff --git a/Source/Core/Windows/BitFlagsForm.resx b/Source/Core/Windows/BitFlagsForm.resx new file mode 100644 index 0000000..debb4b4 --- /dev/null +++ b/Source/Core/Windows/BitFlagsForm.resx @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + True + + + True + + + True + + + True + + \ No newline at end of file diff --git a/Source/Core/Windows/ConfigForm.Designer.cs b/Source/Core/Windows/ConfigForm.Designer.cs new file mode 100644 index 0000000..eafe3e0 --- /dev/null +++ b/Source/Core/Windows/ConfigForm.Designer.cs @@ -0,0 +1,740 @@ +namespace CodeImp.DoomBuilder.Windows +{ + partial class ConfigForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + System.Windows.Forms.Label label5; + System.Windows.Forms.Label label6; + System.Windows.Forms.Label label3; + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(ConfigForm)); + System.Windows.Forms.Label label2; + System.Windows.Forms.Label label7; + System.Windows.Forms.Label label9; + System.Windows.Forms.Label label1; + System.Windows.Forms.Label label8; + System.Windows.Forms.Label label4; + System.Windows.Forms.Label label10; + this.labelparameters = new System.Windows.Forms.Label(); + this.cancel = new System.Windows.Forms.Button(); + this.apply = new System.Windows.Forms.Button(); + this.tabs = new System.Windows.Forms.TabControl(); + this.tabresources = new System.Windows.Forms.TabPage(); + this.configdata = new CodeImp.DoomBuilder.Controls.ResourceListEditor(); + this.tabnodebuilder = new System.Windows.Forms.TabPage(); + this.nodebuildertest = new System.Windows.Forms.ComboBox(); + this.nodebuildersave = new System.Windows.Forms.ComboBox(); + this.tabtesting = new System.Windows.Forms.TabPage(); + this.shortpaths = new System.Windows.Forms.CheckBox(); + this.customparameters = new System.Windows.Forms.CheckBox(); + this.skill = new CodeImp.DoomBuilder.Controls.ActionSelectorControl(); + this.browsetestprogram = new System.Windows.Forms.Button(); + this.noresultlabel = new System.Windows.Forms.Label(); + this.testresult = new System.Windows.Forms.TextBox(); + this.labelresult = new System.Windows.Forms.Label(); + this.testparameters = new System.Windows.Forms.TextBox(); + this.testapplication = new System.Windows.Forms.TextBox(); + this.tabtextures = new System.Windows.Forms.TabPage(); + this.listtextures = new System.Windows.Forms.ListView(); + this.smallimages = new System.Windows.Forms.ImageList(this.components); + this.restoretexturesets = new System.Windows.Forms.Button(); + this.edittextureset = new System.Windows.Forms.Button(); + this.pastetexturesets = new System.Windows.Forms.Button(); + this.copytexturesets = new System.Windows.Forms.Button(); + this.removetextureset = new System.Windows.Forms.Button(); + this.addtextureset = new System.Windows.Forms.Button(); + this.tabmodes = new System.Windows.Forms.TabPage(); + this.startmode = new System.Windows.Forms.ComboBox(); + this.label11 = new System.Windows.Forms.Label(); + this.listmodes = new System.Windows.Forms.ListView(); + this.colmodename = new System.Windows.Forms.ColumnHeader(); + this.colmodeplugin = new System.Windows.Forms.ColumnHeader(); + this.listconfigs = new System.Windows.Forms.ListView(); + this.columnname = new System.Windows.Forms.ColumnHeader(); + this.testprogramdialog = new System.Windows.Forms.OpenFileDialog(); + label5 = new System.Windows.Forms.Label(); + label6 = new System.Windows.Forms.Label(); + label3 = new System.Windows.Forms.Label(); + label2 = new System.Windows.Forms.Label(); + label7 = new System.Windows.Forms.Label(); + label9 = new System.Windows.Forms.Label(); + label1 = new System.Windows.Forms.Label(); + label8 = new System.Windows.Forms.Label(); + label4 = new System.Windows.Forms.Label(); + label10 = new System.Windows.Forms.Label(); + this.tabs.SuspendLayout(); + this.tabresources.SuspendLayout(); + this.tabnodebuilder.SuspendLayout(); + this.tabtesting.SuspendLayout(); + this.tabtextures.SuspendLayout(); + this.tabmodes.SuspendLayout(); + this.SuspendLayout(); + // + // label5 + // + label5.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + label5.AutoSize = true; + label5.Location = new System.Drawing.Point(12, 276); + label5.Name = "label5"; + label5.Size = new System.Drawing.Size(312, 14); + label5.TabIndex = 19; + label5.Text = "Drag items to change order (lower items override higher items)."; + // + // label6 + // + label6.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + label6.AutoEllipsis = true; + label6.Location = new System.Drawing.Point(12, 15); + label6.Name = "label6"; + label6.Size = new System.Drawing.Size(452, 37); + label6.TabIndex = 21; + label6.Text = "These are the resources that will be loaded when this configuration is chosen for" + + " editing. Usually you add your IWAD (like doom.wad or doom2.wad) here."; + // + // label3 + // + label3.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + label3.AutoEllipsis = true; + label3.Location = new System.Drawing.Point(12, 15); + label3.Name = "label3"; + label3.Size = new System.Drawing.Size(443, 54); + label3.TabIndex = 22; + label3.Text = resources.GetString("label3.Text"); + // + // label2 + // + label2.AutoSize = true; + label2.Location = new System.Drawing.Point(12, 86); + label2.Name = "label2"; + label2.Size = new System.Drawing.Size(149, 14); + label2.TabIndex = 24; + label2.Text = "Configuration for saving map:"; + // + // label7 + // + label7.AutoSize = true; + label7.Location = new System.Drawing.Point(35, 125); + label7.Name = "label7"; + label7.Size = new System.Drawing.Size(126, 14); + label7.TabIndex = 26; + label7.Text = "Configuration for testing:"; + // + // label9 + // + label9.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + label9.AutoEllipsis = true; + label9.Location = new System.Drawing.Point(12, 15); + label9.Name = "label9"; + label9.Size = new System.Drawing.Size(452, 54); + label9.TabIndex = 23; + label9.Text = "Here you can specify the program settings to use for launching a game engine when" + + " testing the map. Press F1 for help with custom parameters."; + // + // label1 + // + label1.AutoSize = true; + label1.Location = new System.Drawing.Point(15, 62); + label1.Name = "label1"; + label1.Size = new System.Drawing.Size(63, 14); + label1.TabIndex = 24; + label1.Text = "Application:"; + // + // label8 + // + label8.AutoSize = true; + label8.Location = new System.Drawing.Point(21, 97); + label8.Name = "label8"; + label8.Size = new System.Drawing.Size(57, 14); + label8.TabIndex = 34; + label8.Text = "Skill Level:"; + // + // label4 + // + label4.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + label4.AutoEllipsis = true; + label4.Location = new System.Drawing.Point(12, 15); + label4.Name = "label4"; + label4.Size = new System.Drawing.Size(458, 46); + label4.TabIndex = 24; + label4.Text = "Texture Sets are a way to group textures and flats into categories, so that you c" + + "an easily find a texture for the specific style or purpose you need by selecting" + + " one of the categories."; + // + // label10 + // + label10.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + label10.AutoEllipsis = true; + label10.Location = new System.Drawing.Point(12, 15); + label10.Name = "label10"; + label10.Size = new System.Drawing.Size(445, 58); + label10.TabIndex = 25; + label10.Text = resources.GetString("label10.Text"); + // + // labelparameters + // + this.labelparameters.AutoSize = true; + this.labelparameters.Location = new System.Drawing.Point(15, 159); + this.labelparameters.Name = "labelparameters"; + this.labelparameters.Size = new System.Drawing.Size(65, 14); + this.labelparameters.TabIndex = 27; + this.labelparameters.Text = "Parameters:"; + this.labelparameters.Visible = false; + // + // cancel + // + this.cancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.cancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.cancel.Location = new System.Drawing.Point(617, 381); + this.cancel.Name = "cancel"; + this.cancel.Size = new System.Drawing.Size(112, 25); + this.cancel.TabIndex = 3; + this.cancel.Text = "Cancel"; + this.cancel.UseVisualStyleBackColor = true; + this.cancel.Click += new System.EventHandler(this.cancel_Click); + // + // apply + // + this.apply.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.apply.Location = new System.Drawing.Point(499, 381); + this.apply.Name = "apply"; + this.apply.Size = new System.Drawing.Size(112, 25); + this.apply.TabIndex = 2; + this.apply.Text = "OK"; + this.apply.UseVisualStyleBackColor = true; + this.apply.Click += new System.EventHandler(this.apply_Click); + // + // tabs + // + this.tabs.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.tabs.Controls.Add(this.tabresources); + this.tabs.Controls.Add(this.tabnodebuilder); + this.tabs.Controls.Add(this.tabtesting); + this.tabs.Controls.Add(this.tabtextures); + this.tabs.Controls.Add(this.tabmodes); + this.tabs.Enabled = false; + this.tabs.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tabs.ItemSize = new System.Drawing.Size(100, 19); + this.tabs.Location = new System.Drawing.Point(248, 12); + this.tabs.Name = "tabs"; + this.tabs.Padding = new System.Drawing.Point(20, 3); + this.tabs.SelectedIndex = 0; + this.tabs.Size = new System.Drawing.Size(481, 358); + this.tabs.TabIndex = 1; + // + // tabresources + // + this.tabresources.Controls.Add(label6); + this.tabresources.Controls.Add(this.configdata); + this.tabresources.Controls.Add(label5); + this.tabresources.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tabresources.Location = new System.Drawing.Point(4, 23); + this.tabresources.Name = "tabresources"; + this.tabresources.Padding = new System.Windows.Forms.Padding(6); + this.tabresources.Size = new System.Drawing.Size(473, 331); + this.tabresources.TabIndex = 0; + this.tabresources.Text = "Resources"; + this.tabresources.UseVisualStyleBackColor = true; + // + // configdata + // + this.configdata.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.configdata.DialogOffset = new System.Drawing.Point(-120, 10); + this.configdata.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.configdata.Location = new System.Drawing.Point(15, 55); + this.configdata.Name = "configdata"; + this.configdata.Size = new System.Drawing.Size(440, 208); + this.configdata.TabIndex = 0; + this.configdata.OnContentChanged += new CodeImp.DoomBuilder.Controls.ResourceListEditor.ContentChanged(this.resourcelocations_OnContentChanged); + // + // tabnodebuilder + // + this.tabnodebuilder.Controls.Add(label7); + this.tabnodebuilder.Controls.Add(this.nodebuildertest); + this.tabnodebuilder.Controls.Add(label2); + this.tabnodebuilder.Controls.Add(this.nodebuildersave); + this.tabnodebuilder.Controls.Add(label3); + this.tabnodebuilder.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tabnodebuilder.Location = new System.Drawing.Point(4, 23); + this.tabnodebuilder.Name = "tabnodebuilder"; + this.tabnodebuilder.Padding = new System.Windows.Forms.Padding(6); + this.tabnodebuilder.Size = new System.Drawing.Size(473, 331); + this.tabnodebuilder.TabIndex = 1; + this.tabnodebuilder.Text = "Nodebuilder"; + this.tabnodebuilder.UseVisualStyleBackColor = true; + // + // nodebuildertest + // + this.nodebuildertest.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.nodebuildertest.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.nodebuildertest.FormattingEnabled = true; + this.nodebuildertest.Location = new System.Drawing.Point(167, 122); + this.nodebuildertest.Name = "nodebuildertest"; + this.nodebuildertest.Size = new System.Drawing.Size(288, 22); + this.nodebuildertest.Sorted = true; + this.nodebuildertest.TabIndex = 1; + this.nodebuildertest.SelectedIndexChanged += new System.EventHandler(this.nodebuildertest_SelectedIndexChanged); + // + // nodebuildersave + // + this.nodebuildersave.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.nodebuildersave.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.nodebuildersave.FormattingEnabled = true; + this.nodebuildersave.Location = new System.Drawing.Point(167, 83); + this.nodebuildersave.Name = "nodebuildersave"; + this.nodebuildersave.Size = new System.Drawing.Size(288, 22); + this.nodebuildersave.Sorted = true; + this.nodebuildersave.TabIndex = 0; + this.nodebuildersave.SelectedIndexChanged += new System.EventHandler(this.nodebuildersave_SelectedIndexChanged); + // + // tabtesting + // + this.tabtesting.Controls.Add(this.shortpaths); + this.tabtesting.Controls.Add(this.customparameters); + this.tabtesting.Controls.Add(this.skill); + this.tabtesting.Controls.Add(label8); + this.tabtesting.Controls.Add(this.browsetestprogram); + this.tabtesting.Controls.Add(this.noresultlabel); + this.tabtesting.Controls.Add(this.testresult); + this.tabtesting.Controls.Add(this.labelresult); + this.tabtesting.Controls.Add(this.testparameters); + this.tabtesting.Controls.Add(this.labelparameters); + this.tabtesting.Controls.Add(this.testapplication); + this.tabtesting.Controls.Add(label1); + this.tabtesting.Controls.Add(label9); + this.tabtesting.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tabtesting.Location = new System.Drawing.Point(4, 23); + this.tabtesting.Name = "tabtesting"; + this.tabtesting.Padding = new System.Windows.Forms.Padding(6); + this.tabtesting.Size = new System.Drawing.Size(473, 331); + this.tabtesting.TabIndex = 2; + this.tabtesting.Text = "Testing"; + this.tabtesting.UseVisualStyleBackColor = true; + // + // shortpaths + // + this.shortpaths.AutoSize = true; + this.shortpaths.Location = new System.Drawing.Point(87, 203); + this.shortpaths.Name = "shortpaths"; + this.shortpaths.Size = new System.Drawing.Size(276, 18); + this.shortpaths.TabIndex = 5; + this.shortpaths.Text = "Use short paths and file names (MSDOS 8.3 format)"; + this.shortpaths.UseVisualStyleBackColor = true; + this.shortpaths.Visible = false; + this.shortpaths.CheckedChanged += new System.EventHandler(this.shortpaths_CheckedChanged); + // + // customparameters + // + this.customparameters.AutoSize = true; + this.customparameters.Location = new System.Drawing.Point(86, 132); + this.customparameters.Name = "customparameters"; + this.customparameters.Size = new System.Drawing.Size(134, 18); + this.customparameters.TabIndex = 3; + this.customparameters.Text = "Customize parameters"; + this.customparameters.UseVisualStyleBackColor = true; + this.customparameters.CheckedChanged += new System.EventHandler(this.customparameters_CheckedChanged); + // + // skill + // + this.skill.BackColor = System.Drawing.Color.Transparent; + this.skill.Cursor = System.Windows.Forms.Cursors.Default; + this.skill.Empty = false; + this.skill.GeneralizedCategories = null; + this.skill.Location = new System.Drawing.Point(87, 94); + this.skill.Name = "skill"; + this.skill.Size = new System.Drawing.Size(329, 21); + this.skill.TabIndex = 2; + this.skill.Value = 402; + this.skill.ValueChanges += new System.EventHandler(this.skill_ValueChanges); + // + // browsetestprogram + // + this.browsetestprogram.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.browsetestprogram.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.browsetestprogram.Image = global::CodeImp.DoomBuilder.Properties.Resources.Folder; + this.browsetestprogram.Location = new System.Drawing.Point(422, 58); + this.browsetestprogram.Name = "browsetestprogram"; + this.browsetestprogram.Padding = new System.Windows.Forms.Padding(0, 0, 1, 3); + this.browsetestprogram.Size = new System.Drawing.Size(30, 23); + this.browsetestprogram.TabIndex = 1; + this.browsetestprogram.Text = " "; + this.browsetestprogram.UseVisualStyleBackColor = true; + this.browsetestprogram.Click += new System.EventHandler(this.browsetestprogram_Click); + // + // noresultlabel + // + this.noresultlabel.Location = new System.Drawing.Point(84, 236); + this.noresultlabel.Name = "noresultlabel"; + this.noresultlabel.Size = new System.Drawing.Size(272, 43); + this.noresultlabel.TabIndex = 32; + this.noresultlabel.Text = "An example result cannot be displayed, because it requires a map to be loaded."; + this.noresultlabel.Visible = false; + // + // testresult + // + this.testresult.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.testresult.BackColor = System.Drawing.SystemColors.Control; + this.testresult.Location = new System.Drawing.Point(86, 233); + this.testresult.Multiline = true; + this.testresult.Name = "testresult"; + this.testresult.ReadOnly = true; + this.testresult.Size = new System.Drawing.Size(366, 79); + this.testresult.TabIndex = 6; + this.testresult.Visible = false; + // + // labelresult + // + this.labelresult.AutoSize = true; + this.labelresult.Location = new System.Drawing.Point(38, 236); + this.labelresult.Name = "labelresult"; + this.labelresult.Size = new System.Drawing.Size(40, 14); + this.labelresult.TabIndex = 30; + this.labelresult.Text = "Result:"; + this.labelresult.Visible = false; + // + // testparameters + // + this.testparameters.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.testparameters.Location = new System.Drawing.Point(86, 156); + this.testparameters.Multiline = true; + this.testparameters.Name = "testparameters"; + this.testparameters.Size = new System.Drawing.Size(366, 41); + this.testparameters.TabIndex = 4; + this.testparameters.Visible = false; + this.testparameters.TextChanged += new System.EventHandler(this.testparameters_TextChanged); + // + // testapplication + // + this.testapplication.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.testapplication.Location = new System.Drawing.Point(86, 59); + this.testapplication.Name = "testapplication"; + this.testapplication.ReadOnly = true; + this.testapplication.Size = new System.Drawing.Size(330, 20); + this.testapplication.TabIndex = 0; + this.testapplication.TextChanged += new System.EventHandler(this.testapplication_TextChanged); + // + // tabtextures + // + this.tabtextures.Controls.Add(this.listtextures); + this.tabtextures.Controls.Add(this.restoretexturesets); + this.tabtextures.Controls.Add(this.edittextureset); + this.tabtextures.Controls.Add(this.pastetexturesets); + this.tabtextures.Controls.Add(this.copytexturesets); + this.tabtextures.Controls.Add(this.removetextureset); + this.tabtextures.Controls.Add(this.addtextureset); + this.tabtextures.Controls.Add(label4); + this.tabtextures.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tabtextures.Location = new System.Drawing.Point(4, 23); + this.tabtextures.Name = "tabtextures"; + this.tabtextures.Size = new System.Drawing.Size(473, 331); + this.tabtextures.TabIndex = 3; + this.tabtextures.Text = "Textures"; + this.tabtextures.UseVisualStyleBackColor = true; + // + // listtextures + // + this.listtextures.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.listtextures.FullRowSelect = true; + this.listtextures.HideSelection = false; + this.listtextures.Location = new System.Drawing.Point(15, 64); + this.listtextures.Name = "listtextures"; + this.listtextures.ShowGroups = false; + this.listtextures.Size = new System.Drawing.Size(442, 175); + this.listtextures.SmallImageList = this.smallimages; + this.listtextures.Sorting = System.Windows.Forms.SortOrder.Ascending; + this.listtextures.TabIndex = 0; + this.listtextures.UseCompatibleStateImageBehavior = false; + this.listtextures.View = System.Windows.Forms.View.List; + this.listtextures.SelectedIndexChanged += new System.EventHandler(this.listtextures_SelectedIndexChanged); + this.listtextures.DoubleClick += new System.EventHandler(this.listtextures_DoubleClick); + // + // smallimages + // + this.smallimages.ImageStream = ((System.Windows.Forms.ImageListStreamer)(resources.GetObject("smallimages.ImageStream"))); + this.smallimages.TransparentColor = System.Drawing.Color.Transparent; + this.smallimages.Images.SetKeyName(0, "KnownTextureSet.ico"); + // + // restoretexturesets + // + this.restoretexturesets.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.restoretexturesets.Location = new System.Drawing.Point(15, 283); + this.restoretexturesets.Name = "restoretexturesets"; + this.restoretexturesets.Size = new System.Drawing.Size(140, 24); + this.restoretexturesets.TabIndex = 6; + this.restoretexturesets.Text = "Add Default Sets"; + this.restoretexturesets.UseVisualStyleBackColor = true; + this.restoretexturesets.Click += new System.EventHandler(this.restoretexturesets_Click); + // + // edittextureset + // + this.edittextureset.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.edittextureset.Enabled = false; + this.edittextureset.Location = new System.Drawing.Point(88, 245); + this.edittextureset.Name = "edittextureset"; + this.edittextureset.Size = new System.Drawing.Size(67, 24); + this.edittextureset.TabIndex = 2; + this.edittextureset.Text = "Edit..."; + this.edittextureset.UseVisualStyleBackColor = true; + this.edittextureset.Click += new System.EventHandler(this.edittextureset_Click); + // + // pastetexturesets + // + this.pastetexturesets.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.pastetexturesets.Enabled = false; + this.pastetexturesets.Location = new System.Drawing.Point(399, 245); + this.pastetexturesets.Name = "pastetexturesets"; + this.pastetexturesets.Size = new System.Drawing.Size(58, 24); + this.pastetexturesets.TabIndex = 5; + this.pastetexturesets.Text = "Paste"; + this.pastetexturesets.UseVisualStyleBackColor = true; + this.pastetexturesets.Click += new System.EventHandler(this.pastetexturesets_Click); + // + // copytexturesets + // + this.copytexturesets.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.copytexturesets.Enabled = false; + this.copytexturesets.Location = new System.Drawing.Point(335, 245); + this.copytexturesets.Name = "copytexturesets"; + this.copytexturesets.Size = new System.Drawing.Size(58, 24); + this.copytexturesets.TabIndex = 4; + this.copytexturesets.Text = "Copy"; + this.copytexturesets.UseVisualStyleBackColor = true; + this.copytexturesets.Click += new System.EventHandler(this.copytexturesets_Click); + // + // removetextureset + // + this.removetextureset.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.removetextureset.Enabled = false; + this.removetextureset.Location = new System.Drawing.Point(161, 245); + this.removetextureset.Name = "removetextureset"; + this.removetextureset.Size = new System.Drawing.Size(68, 24); + this.removetextureset.TabIndex = 3; + this.removetextureset.Text = "Remove"; + this.removetextureset.UseVisualStyleBackColor = true; + this.removetextureset.Click += new System.EventHandler(this.removetextureset_Click); + // + // addtextureset + // + this.addtextureset.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.addtextureset.Location = new System.Drawing.Point(15, 245); + this.addtextureset.Name = "addtextureset"; + this.addtextureset.Size = new System.Drawing.Size(67, 24); + this.addtextureset.TabIndex = 1; + this.addtextureset.Text = "Add..."; + this.addtextureset.UseVisualStyleBackColor = true; + this.addtextureset.Click += new System.EventHandler(this.addtextureset_Click); + // + // tabmodes + // + this.tabmodes.Controls.Add(this.startmode); + this.tabmodes.Controls.Add(this.label11); + this.tabmodes.Controls.Add(this.listmodes); + this.tabmodes.Controls.Add(label10); + this.tabmodes.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tabmodes.Location = new System.Drawing.Point(4, 23); + this.tabmodes.Name = "tabmodes"; + this.tabmodes.Size = new System.Drawing.Size(473, 331); + this.tabmodes.TabIndex = 4; + this.tabmodes.Text = "Modes"; + this.tabmodes.UseVisualStyleBackColor = true; + // + // startmode + // + this.startmode.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.startmode.FormattingEnabled = true; + this.startmode.Location = new System.Drawing.Point(239, 288); + this.startmode.Name = "startmode"; + this.startmode.Size = new System.Drawing.Size(218, 22); + this.startmode.TabIndex = 27; + this.startmode.SelectedIndexChanged += new System.EventHandler(this.startmode_SelectedIndexChanged); + // + // label11 + // + this.label11.AutoSize = true; + this.label11.Location = new System.Drawing.Point(12, 291); + this.label11.Name = "label11"; + this.label11.Size = new System.Drawing.Size(199, 14); + this.label11.TabIndex = 26; + this.label11.Text = "When opening a map, start in this mode:"; + // + // listmodes + // + this.listmodes.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.listmodes.CheckBoxes = true; + this.listmodes.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.colmodename, + this.colmodeplugin}); + this.listmodes.FullRowSelect = true; + this.listmodes.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.Nonclickable; + this.listmodes.Location = new System.Drawing.Point(15, 70); + this.listmodes.MultiSelect = false; + this.listmodes.Name = "listmodes"; + this.listmodes.ShowGroups = false; + this.listmodes.Size = new System.Drawing.Size(442, 202); + this.listmodes.Sorting = System.Windows.Forms.SortOrder.Ascending; + this.listmodes.TabIndex = 0; + this.listmodes.UseCompatibleStateImageBehavior = false; + this.listmodes.View = System.Windows.Forms.View.Details; + this.listmodes.ItemChecked += new System.Windows.Forms.ItemCheckedEventHandler(this.listmodes_ItemChecked); + // + // colmodename + // + this.colmodename.Text = "Editing Mode"; + this.colmodename.Width = 179; + // + // colmodeplugin + // + this.colmodeplugin.Text = "Plugin"; + this.colmodeplugin.Width = 221; + // + // listconfigs + // + this.listconfigs.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left))); + this.listconfigs.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.columnname}); + this.listconfigs.FullRowSelect = true; + this.listconfigs.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.None; + this.listconfigs.HideSelection = false; + this.listconfigs.Location = new System.Drawing.Point(12, 12); + this.listconfigs.MultiSelect = false; + this.listconfigs.Name = "listconfigs"; + this.listconfigs.ShowGroups = false; + this.listconfigs.Size = new System.Drawing.Size(230, 358); + this.listconfigs.Sorting = System.Windows.Forms.SortOrder.Ascending; + this.listconfigs.TabIndex = 0; + this.listconfigs.UseCompatibleStateImageBehavior = false; + this.listconfigs.View = System.Windows.Forms.View.Details; + this.listconfigs.SelectedIndexChanged += new System.EventHandler(this.listconfigs_SelectedIndexChanged); + this.listconfigs.MouseUp += new System.Windows.Forms.MouseEventHandler(this.listconfigs_MouseUp); + this.listconfigs.KeyUp += new System.Windows.Forms.KeyEventHandler(this.listconfigs_KeyUp); + // + // columnname + // + this.columnname.Text = "Configuration"; + this.columnname.Width = 200; + // + // testprogramdialog + // + this.testprogramdialog.Filter = "Executable Files (*.exe)|*.exe|Batch Files (*.bat)|*.bat"; + this.testprogramdialog.Title = "Browse Test Program"; + // + // ConfigForm + // + this.AcceptButton = this.apply; + this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; + this.CancelButton = this.cancel; + this.ClientSize = new System.Drawing.Size(740, 416); + this.Controls.Add(this.listconfigs); + this.Controls.Add(this.tabs); + this.Controls.Add(this.cancel); + this.Controls.Add(this.apply); + this.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "ConfigForm"; + this.Opacity = 0; + this.ShowIcon = false; + this.ShowInTaskbar = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Game Configurations"; + this.HelpRequested += new System.Windows.Forms.HelpEventHandler(this.ConfigForm_HelpRequested); + this.tabs.ResumeLayout(false); + this.tabresources.ResumeLayout(false); + this.tabresources.PerformLayout(); + this.tabnodebuilder.ResumeLayout(false); + this.tabnodebuilder.PerformLayout(); + this.tabtesting.ResumeLayout(false); + this.tabtesting.PerformLayout(); + this.tabtextures.ResumeLayout(false); + this.tabmodes.ResumeLayout(false); + this.tabmodes.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.Button cancel; + private System.Windows.Forms.Button apply; + private System.Windows.Forms.TabControl tabs; + private System.Windows.Forms.TabPage tabresources; + private System.Windows.Forms.TabPage tabnodebuilder; + private System.Windows.Forms.TabPage tabtesting; + private CodeImp.DoomBuilder.Controls.ResourceListEditor configdata; + private System.Windows.Forms.ComboBox nodebuildertest; + private System.Windows.Forms.ComboBox nodebuildersave; + private System.Windows.Forms.TextBox testparameters; + private System.Windows.Forms.TextBox testapplication; + private System.Windows.Forms.TextBox testresult; + private System.Windows.Forms.Label labelresult; + private System.Windows.Forms.ListView listconfigs; + private System.Windows.Forms.ColumnHeader columnname; + private System.Windows.Forms.Label noresultlabel; + private System.Windows.Forms.Button browsetestprogram; + private System.Windows.Forms.OpenFileDialog testprogramdialog; + private System.Windows.Forms.CheckBox customparameters; + private CodeImp.DoomBuilder.Controls.ActionSelectorControl skill; + private System.Windows.Forms.Label labelparameters; + private System.Windows.Forms.TabPage tabtextures; + private System.Windows.Forms.Button addtextureset; + private System.Windows.Forms.Button restoretexturesets; + private System.Windows.Forms.Button edittextureset; + private System.Windows.Forms.Button pastetexturesets; + private System.Windows.Forms.Button copytexturesets; + private System.Windows.Forms.Button removetextureset; + private System.Windows.Forms.ListView listtextures; + private System.Windows.Forms.ImageList smallimages; + private System.Windows.Forms.TabPage tabmodes; + private System.Windows.Forms.ListView listmodes; + private System.Windows.Forms.ColumnHeader colmodename; + private System.Windows.Forms.ColumnHeader colmodeplugin; + private System.Windows.Forms.CheckBox shortpaths; + private System.Windows.Forms.ComboBox startmode; + private System.Windows.Forms.Label label11; + } +} \ No newline at end of file diff --git a/Source/Core/Windows/ConfigForm.cs b/Source/Core/Windows/ConfigForm.cs new file mode 100644 index 0000000..378342b --- /dev/null +++ b/Source/Core/Windows/ConfigForm.cs @@ -0,0 +1,596 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Text; +using System.Windows.Forms; +using Microsoft.Win32; +using System.Diagnostics; +using CodeImp.DoomBuilder.Actions; +using CodeImp.DoomBuilder.Data; +using CodeImp.DoomBuilder.Config; +using System.IO; +using CodeImp.DoomBuilder.Controls; +using CodeImp.DoomBuilder.Editing; + +#endregion + +namespace CodeImp.DoomBuilder.Windows +{ + internal partial class ConfigForm : DelayedForm + { + // Variables + private GameConfiguration gameconfig; + private ConfigurationInfo configinfo; + private List copiedsets; + private bool preventchanges = false; + private bool reloadresources = false; + + // Properties + public bool ReloadResources { get { return reloadresources; } } + + // Constructor + public ConfigForm() + { + ListViewItem lvi; + + // Initialize + InitializeComponent(); + + // Make list column header full width + columnname.Width = listconfigs.ClientRectangle.Width - SystemInformation.VerticalScrollBarWidth - 2; + + // Fill list of configurations + foreach (ConfigurationInfo ci in General.Configs) + { + // Add a copy + lvi = listconfigs.Items.Add(ci.Name); + lvi.Tag = ci.Clone(); + + // This is the current configuration? + if ((General.Map != null) && (General.Map.ConfigSettings.Filename == ci.Filename)) + lvi.Selected = true; + } + + // No skill + skill.Value = 0; + + // Nodebuilders are allowed to be empty + nodebuildersave.Items.Add(new NodebuilderInfo()); + nodebuildertest.Items.Add(new NodebuilderInfo()); + + // Fill comboboxes with nodebuilders + nodebuildersave.Items.AddRange(General.Nodebuilders.ToArray()); + nodebuildertest.Items.AddRange(General.Nodebuilders.ToArray()); + + // Fill list of editing modes + foreach (EditModeInfo emi in General.Editing.ModesInfo) + { + // Is this mode selectable by the user? + if (emi.IsOptional) + { + lvi = listmodes.Items.Add(emi.Attributes.DisplayName); + lvi.Tag = emi; + lvi.SubItems.Add(emi.Plugin.Plug.Name); + } + } + } + + // This shows a specific page + public void ShowTab(int index) + { + tabs.SelectedIndex = index; + } + + // Configuration item selected + private void listconfigs_SelectedIndexChanged(object sender, EventArgs e) + { + NodebuilderInfo ni; + + // Item selected? + if (listconfigs.SelectedItems.Count > 0) + { + // Enable panels + tabs.Enabled = true; + + preventchanges = true; + + // Get config info of selected item + configinfo = listconfigs.SelectedItems[0].Tag as ConfigurationInfo; + + // Load the game configuration + gameconfig = new GameConfiguration(General.LoadGameConfiguration(configinfo.Filename)); + + // Set defaults + configinfo.ApplyDefaults(gameconfig); + + // Fill resources list + configdata.EditResourceLocationList(configinfo.Resources); + + // Go for all nodebuilder save items + nodebuildersave.SelectedIndex = -1; + for (int i = 0; i < nodebuildersave.Items.Count; i++) + { + // Get item + ni = nodebuildersave.Items[i] as NodebuilderInfo; + + // Item matches configuration setting? + if (string.Compare(ni.Name, configinfo.NodebuilderSave, false) == 0) + { + // Select this item + nodebuildersave.SelectedIndex = i; + break; + } + } + + // Go for all nodebuilder test items + nodebuildertest.SelectedIndex = -1; + for (int i = 0; i < nodebuildertest.Items.Count; i++) + { + // Get item + ni = nodebuildertest.Items[i] as NodebuilderInfo; + + // Item matches configuration setting? + if (string.Compare(ni.Name, configinfo.NodebuilderTest, false) == 0) + { + // Select this item + nodebuildertest.SelectedIndex = i; + break; + } + } + + // Fill skills list + skill.ClearInfo(); + skill.AddInfo(gameconfig.Skills.ToArray()); + + // Set test application and parameters + if (!configinfo.CustomParameters) + { + configinfo.TestParameters = gameconfig.TestParameters; + configinfo.TestShortPaths = gameconfig.TestShortPaths; + } + testapplication.Text = configinfo.TestProgram; + testparameters.Text = configinfo.TestParameters; + shortpaths.Checked = configinfo.TestShortPaths; + int skilllevel = configinfo.TestSkill; + skill.Value = skilllevel - 1; + skill.Value = skilllevel; + customparameters.Checked = configinfo.CustomParameters; + + // Fill texture sets list + listtextures.Items.Clear(); + foreach (DefinedTextureSet ts in configinfo.TextureSets) + { + ListViewItem item = listtextures.Items.Add(ts.Name); + item.Tag = ts; + item.ImageIndex = 0; + } + listtextures.Sort(); + + // Go for all the editing modes in the list + foreach (ListViewItem lvi in listmodes.Items) + { + EditModeInfo emi = (lvi.Tag as EditModeInfo); + lvi.Checked = (configinfo.EditModes.ContainsKey(emi.Type.FullName) && configinfo.EditModes[emi.Type.FullName]); + } + + // Fill start modes + RefillStartModes(); + + // Done + preventchanges = false; + } + } + + // Key released + private void listconfigs_KeyUp(object sender, KeyEventArgs e) + { + // Nothing selected? + if (listconfigs.SelectedItems.Count == 0) + { + // Disable panels + gameconfig = null; + configinfo = null; + configdata.FixedResourceLocationList(new DataLocationList()); + configdata.EditResourceLocationList(new DataLocationList()); + nodebuildersave.SelectedIndex = -1; + nodebuildertest.SelectedIndex = -1; + testapplication.Text = ""; + testparameters.Text = ""; + shortpaths.Checked = false; + skill.Value = 0; + skill.ClearInfo(); + customparameters.Checked = false; + tabs.Enabled = false; + listtextures.Items.Clear(); + } + } + + // Mouse released + private void listconfigs_MouseUp(object sender, MouseEventArgs e) + { + listconfigs_KeyUp(sender, new KeyEventArgs(Keys.None)); + } + + // Resource locations changed + private void resourcelocations_OnContentChanged() + { + // Leave when no configuration selected + if (configinfo == null) return; + + // Apply to selected configuration + configinfo.Resources.Clear(); + configinfo.Resources.AddRange(configdata.GetResources()); + if (!preventchanges) reloadresources = true; + } + + // Nodebuilder selection changed + private void nodebuildersave_SelectedIndexChanged(object sender, EventArgs e) + { + // Leave when no configuration selected + if (configinfo == null) return; + + // Apply to selected configuration + if (nodebuildersave.SelectedItem != null) + configinfo.NodebuilderSave = (nodebuildersave.SelectedItem as NodebuilderInfo).Name; + } + + // Nodebuilder selection changed + private void nodebuildertest_SelectedIndexChanged(object sender, EventArgs e) + { + // Leave when no configuration selected + if (configinfo == null) return; + + // Apply to selected configuration + if (nodebuildertest.SelectedItem != null) + configinfo.NodebuilderTest = (nodebuildertest.SelectedItem as NodebuilderInfo).Name; + } + + // Test application changed + private void testapplication_TextChanged(object sender, EventArgs e) + { + // Leave when no configuration selected + if (configinfo == null) return; + + // Apply to selected configuration + configinfo.TestProgram = testapplication.Text; + } + + // Test parameters changed + private void testparameters_TextChanged(object sender, EventArgs e) + { + // Leave when no configuration selected + if (configinfo == null) return; + + // Apply to selected configuration + configinfo = listconfigs.SelectedItems[0].Tag as ConfigurationInfo; + configinfo.TestParameters = testparameters.Text; + + // Show example result + CreateParametersExample(); + } + + // This creates a new parameters example + private void CreateParametersExample() + { + // Map loaded? + if (General.Map != null) + { + // Make converted parameters + testresult.Text = General.Map.Launcher.ConvertParameters(testparameters.Text, skill.Value, shortpaths.Checked); + } + } + + // OK clicked + private void apply_Click(object sender, EventArgs e) + { + ConfigurationInfo ci; + + // Apply configuration items + foreach (ListViewItem lvi in listconfigs.Items) + { + // Get configuration item + ci = lvi.Tag as ConfigurationInfo; + + // Find same configuration info in originals + foreach (ConfigurationInfo oci in General.Configs) + { + // Apply settings when they match + if (string.Compare(ci.Filename, oci.Filename) == 0) oci.Apply(ci); + } + } + + // Close + this.DialogResult = DialogResult.OK; + this.Close(); + } + + // Cancel clicked + private void cancel_Click(object sender, EventArgs e) + { + // Close + this.DialogResult = DialogResult.Cancel; + this.Close(); + } + + // Browse test program + private void browsetestprogram_Click(object sender, EventArgs e) + { + // Set initial directory + if (testapplication.Text.Length > 0) + { + try { testprogramdialog.InitialDirectory = Path.GetDirectoryName(testapplication.Text); } + catch (Exception) { } + } + + // Browse for test program + if (testprogramdialog.ShowDialog() == DialogResult.OK) + { + // Apply + testapplication.Text = testprogramdialog.FileName; + } + } + + // Customize parameters (un)checked + private void customparameters_CheckedChanged(object sender, EventArgs e) + { + // Leave when no configuration selected + if (configinfo == null) return; + + // Apply to selected configuration + configinfo.CustomParameters = customparameters.Checked; + + // Update interface + labelparameters.Visible = customparameters.Checked; + testparameters.Visible = customparameters.Checked; + shortpaths.Visible = customparameters.Checked; + + // Check if a map is loaded + if (General.Map != null) + { + // Show parameters example result + labelresult.Visible = customparameters.Checked; + testresult.Visible = customparameters.Checked; + noresultlabel.Visible = false; + } + else + { + // Cannot show parameters example result + labelresult.Visible = false; + testresult.Visible = false; + noresultlabel.Visible = customparameters.Checked; + } + } + + // Use short paths changed + private void shortpaths_CheckedChanged(object sender, EventArgs e) + { + // Leave when no configuration selected + if (configinfo == null) return; + + // Apply to selected configuration + configinfo.TestShortPaths = shortpaths.Checked; + + CreateParametersExample(); + } + + // Skill changes + private void skill_ValueChanges(object sender, EventArgs e) + { + // Leave when no configuration selected + if (configinfo == null) return; + + // Apply to selected configuration + configinfo.TestSkill = skill.Value; + + CreateParametersExample(); + } + + // Make new texture set + private void addtextureset_Click(object sender, EventArgs e) + { + DefinedTextureSet s = new DefinedTextureSet("New Texture Set"); + TextureSetForm form = new TextureSetForm(); + form.Setup(s); + if (form.ShowDialog(this) == DialogResult.OK) + { + // Add to texture sets + configinfo.TextureSets.Add(s); + ListViewItem item = listtextures.Items.Add(s.Name); + item.Tag = s; + item.ImageIndex = 0; + listtextures.Sort(); + reloadresources = true; + } + } + + // Edit texture set + private void edittextureset_Click(object sender, EventArgs e) + { + // Texture Set selected? + if (listtextures.SelectedItems.Count > 0) + { + DefinedTextureSet s = (listtextures.SelectedItems[0].Tag as DefinedTextureSet); + TextureSetForm form = new TextureSetForm(); + form.Setup(s); + form.ShowDialog(this); + listtextures.SelectedItems[0].Text = s.Name; + listtextures.Sort(); + reloadresources = true; + } + } + + // Remove texture set + private void removetextureset_Click(object sender, EventArgs e) + { + // Texture Set selected? + while (listtextures.SelectedItems.Count > 0) + { + // Remove from config info and list + DefinedTextureSet s = (listtextures.SelectedItems[0].Tag as DefinedTextureSet); + configinfo.TextureSets.Remove(s); + listtextures.SelectedItems[0].Remove(); + reloadresources = true; + } + } + + // Texture Set selected/deselected + private void listtextures_SelectedIndexChanged(object sender, EventArgs e) + { + edittextureset.Enabled = (listtextures.SelectedItems.Count > 0); + removetextureset.Enabled = (listtextures.SelectedItems.Count > 0); + copytexturesets.Enabled = (listtextures.SelectedItems.Count > 0); + } + + // Doubleclicking a texture set + private void listtextures_DoubleClick(object sender, EventArgs e) + { + edittextureset_Click(sender, e); + } + + // Copy selected texture sets + private void copytexturesets_Click(object sender, EventArgs e) + { + // Make copies + copiedsets = new List(); + foreach (ListViewItem item in listtextures.SelectedItems) + { + DefinedTextureSet s = (item.Tag as DefinedTextureSet); + copiedsets.Add(s.Copy()); + } + + // Enable button + pastetexturesets.Enabled = true; + } + + // Paste copied texture sets + private void pastetexturesets_Click(object sender, EventArgs e) + { + if (copiedsets != null) + { + // Add copies + foreach (DefinedTextureSet ts in copiedsets) + { + DefinedTextureSet s = ts.Copy(); + ListViewItem item = listtextures.Items.Add(s.Name); + item.Tag = s; + item.ImageIndex = 0; + configinfo.TextureSets.Add(s); + } + listtextures.Sort(); + reloadresources = true; + } + } + + // This will add the default sets from game configuration + private void restoretexturesets_Click(object sender, EventArgs e) + { + // Ask nicely first + if (MessageBox.Show(this, "This will add the default Texture Sets from the Game Configuration. Do you want to continue?", + "Add Default Sets", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes) + { + // Add copies + foreach (DefinedTextureSet ts in gameconfig.TextureSets) + { + DefinedTextureSet s = ts.Copy(); + ListViewItem item = listtextures.Items.Add(s.Name); + item.Tag = s; + item.ImageIndex = 0; + configinfo.TextureSets.Add(s); + } + listtextures.Sort(); + reloadresources = true; + } + } + + // This is called when an editing mode item is checked or unchecked + private void listmodes_ItemChecked(object sender, ItemCheckedEventArgs e) + { + // Leave when no configuration selected + if (configinfo == null) return; + + // Apply changes + EditModeInfo emi = (e.Item.Tag as EditModeInfo); + bool currentstate = (configinfo.EditModes.ContainsKey(emi.Type.FullName) && configinfo.EditModes[emi.Type.FullName]); + if (e.Item.Checked && !currentstate) + { + // Add + configinfo.EditModes[emi.Type.FullName] = true; + } + else if (!e.Item.Checked && currentstate) + { + // Remove + configinfo.EditModes[emi.Type.FullName] = false; + } + + preventchanges = true; + RefillStartModes(); + preventchanges = false; + } + + // Help requested + private void ConfigForm_HelpRequested(object sender, HelpEventArgs hlpevent) + { + General.ShowHelp("w_gameconfigurations.html"); + hlpevent.Handled = true; + } + + // This refills the start mode cobobox + private void RefillStartModes() + { + // Refill the startmode combobox + startmode.Items.Clear(); + foreach (ListViewItem item in listmodes.Items) + { + if (item.Checked) + { + EditModeInfo emi = (item.Tag as EditModeInfo); + if (emi.Attributes.SafeStartMode) + { + int newindex = startmode.Items.Add(emi); + if (emi.Type.Name == configinfo.StartMode) startmode.SelectedIndex = newindex; + } + } + } + + // Select the first in the combobox if none are selected + if ((startmode.SelectedItem == null) && (startmode.Items.Count > 0)) + { + startmode.SelectedIndex = 0; + EditModeInfo emi = (startmode.SelectedItem as EditModeInfo); + configinfo.StartMode = emi.Type.Name; + } + } + + // Start mode combobox changed + private void startmode_SelectedIndexChanged(object sender, EventArgs e) + { + if (preventchanges || (configinfo == null)) return; + + // Apply start mode + if (startmode.SelectedItem != null) + { + EditModeInfo emi = (startmode.SelectedItem as EditModeInfo); + configinfo.StartMode = emi.Type.Name; + } + } + } +} diff --git a/Source/Core/Windows/ConfigForm.resx b/Source/Core/Windows/ConfigForm.resx new file mode 100644 index 0000000..8357bb6 --- /dev/null +++ b/Source/Core/Windows/ConfigForm.resx @@ -0,0 +1,334 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + True + + + False + + + True + + + False + + + True + + + False + + + The nodebuilder is a compiler which builds geometry structures for your map. You need these structures to be able to play the map in the game. For each purpose you can choose the desired nodebuilder configuration here. + + + True + + + False + + + True + + + False + + + True + + + False + + + True + + + False + + + True + + + False + + + True + + + False + + + True + + + False + + + Here you can select the editing modes that you wish to use in this configuration. This is useful in case there are plugins with additional editing modes that can be used as a replacement for the original editing modes. + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + 159, 17 + + + + AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj0yLjAuMC4w + LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAACZTeXN0 + ZW0uV2luZG93cy5Gb3Jtcy5JbWFnZUxpc3RTdHJlYW1lcgEAAAAERGF0YQcCAgAAAAkDAAAADwMAAAA4 + CAAAAk1TRnQBSQFMAwEBAAEEAQABBAEAARMBAAEQAQAE/wEJAQAI/wFCAU0BNgEEBgABNgEEAgABKAMA + AUwDAAEQAwABAQEAAQgFAAHAAQQYAAGAAgABgAMAAoABAAGAAwABgAEAAYABAAKAAgADwAEAAcAB3AHA + AQAB8AHKAaYBAAEzBQABMwEAATMBAAEzAQACMwIAAxYBAAMcAQADIgEAAykBAANVAQADTQEAA0IBAAM5 + AQABgAF8Af8BAAJQAf8BAAGTAQAB1gEAAf8B7AHMAQABxgHWAe8BAAHWAucBAAGQAakBrQIAAf8BMwMA + AWYDAAGZAwABzAIAATMDAAIzAgABMwFmAgABMwGZAgABMwHMAgABMwH/AgABZgMAAWYBMwIAAmYCAAFm + AZkCAAFmAcwCAAFmAf8CAAGZAwABmQEzAgABmQFmAgACmQIAAZkBzAIAAZkB/wIAAcwDAAHMATMCAAHM + AWYCAAHMAZkCAALMAgABzAH/AgAB/wFmAgAB/wGZAgAB/wHMAQABMwH/AgAB/wEAATMBAAEzAQABZgEA + ATMBAAGZAQABMwEAAcwBAAEzAQAB/wEAAf8BMwIAAzMBAAIzAWYBAAIzAZkBAAIzAcwBAAIzAf8BAAEz + AWYCAAEzAWYBMwEAATMCZgEAATMBZgGZAQABMwFmAcwBAAEzAWYB/wEAATMBmQIAATMBmQEzAQABMwGZ + AWYBAAEzApkBAAEzAZkBzAEAATMBmQH/AQABMwHMAgABMwHMATMBAAEzAcwBZgEAATMBzAGZAQABMwLM + AQABMwHMAf8BAAEzAf8BMwEAATMB/wFmAQABMwH/AZkBAAEzAf8BzAEAATMC/wEAAWYDAAFmAQABMwEA + AWYBAAFmAQABZgEAAZkBAAFmAQABzAEAAWYBAAH/AQABZgEzAgABZgIzAQABZgEzAWYBAAFmATMBmQEA + AWYBMwHMAQABZgEzAf8BAAJmAgACZgEzAQADZgEAAmYBmQEAAmYBzAEAAWYBmQIAAWYBmQEzAQABZgGZ + AWYBAAFmApkBAAFmAZkBzAEAAWYBmQH/AQABZgHMAgABZgHMATMBAAFmAcwBmQEAAWYCzAEAAWYBzAH/ + AQABZgH/AgABZgH/ATMBAAFmAf8BmQEAAWYB/wHMAQABzAEAAf8BAAH/AQABzAEAApkCAAGZATMBmQEA + AZkBAAGZAQABmQEAAcwBAAGZAwABmQIzAQABmQEAAWYBAAGZATMBzAEAAZkBAAH/AQABmQFmAgABmQFm + ATMBAAGZATMBZgEAAZkBZgGZAQABmQFmAcwBAAGZATMB/wEAApkBMwEAApkBZgEAA5kBAAKZAcwBAAKZ + Af8BAAGZAcwCAAGZAcwBMwEAAWYBzAFmAQABmQHMAZkBAAGZAswBAAGZAcwB/wEAAZkB/wIAAZkB/wEz + AQABmQHMAWYBAAGZAf8BmQEAAZkB/wHMAQABmQL/AQABzAMAAZkBAAEzAQABzAEAAWYBAAHMAQABmQEA + AcwBAAHMAQABmQEzAgABzAIzAQABzAEzAWYBAAHMATMBmQEAAcwBMwHMAQABzAEzAf8BAAHMAWYCAAHM + AWYBMwEAAZkCZgEAAcwBZgGZAQABzAFmAcwBAAGZAWYB/wEAAcwBmQIAAcwBmQEzAQABzAGZAWYBAAHM + ApkBAAHMAZkBzAEAAcwBmQH/AQACzAIAAswBMwEAAswBZgEAAswBmQEAA8wBAALMAf8BAAHMAf8CAAHM + Af8BMwEAAZkB/wFmAQABzAH/AZkBAAHMAf8BzAEAAcwC/wEAAcwBAAEzAQAB/wEAAWYBAAH/AQABmQEA + AcwBMwIAAf8CMwEAAf8BMwFmAQAB/wEzAZkBAAH/ATMBzAEAAf8BMwH/AQAB/wFmAgAB/wFmATMBAAHM + AmYBAAH/AWYBmQEAAf8BZgHMAQABzAFmAf8BAAH/AZkCAAH/AZkBMwEAAf8BmQFmAQAB/wKZAQAB/wGZ + AcwBAAH/AZkB/wEAAf8BzAIAAf8BzAEzAQAB/wHMAWYBAAH/AcwBmQEAAf8CzAEAAf8BzAH/AQAC/wEz + AQABzAH/AWYBAAL/AZkBAAL/AcwBAAJmAf8BAAFmAf8BZgEAAWYC/wEAAf8CZgEAAf8BZgH/AQAC/wFm + AQABIQEAAaUBAANfAQADdwEAA4YBAAOWAQADywEAA7IBAAPXAQAD3QEAA+MBAAPqAQAD8QEAA/gBAAHw + AfsB/wEAAaQCoAEAA4ADAAH/AgAB/wMAAv8BAAH/AwAB/wEAAf8BAAL/AgAD/5sAAfcCrgFtAWwB6gJs + BGZAAAH3Bf8C9AMZAWZAAAG1Av8CkwGZAQcBuwG0AhkDZj4AAbUC/wGTAZoBegGfAZ4BuwH0AfMBZgEZ + AWY+AAG1Av8BuwEIAp8BfgF5AvQBbAEZA2Y8AAG1Av8C2wEJAQgBfgF5Af8B9AFsAfMBZgEZAWY8AAEH + Cv8BbAH0AWwBGQFmPAACBwS1AfcBtQLtApEB9AFsAfMBZj4AAQcK/wFsAfQBbD4AAgcEtQH3AbUC7QKR + AfQBbEAAAQcK/wFsQAACBwS1AfcBtQLtApHSAAFCAU0BPgcAAT4DAAEoAwABTAMAARADAAEBAQABAQUA + AcAXAAP/AQAC/wHgCQAC/wHgCQABwAEDAeAJAAHAAQMB4AkAAcABAAHgCQABwAEAAeAJAAHAAQABIAkA + AcABAAEgCQABwAEAASAJAAHAAQABIAkAAfABAAEgCQAB8AEAASAJAAH8AQABIAkAAfwBAAEgCQAC/wHg + CQAC/wHgCQAL + + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + 17, 17 + + + True + + \ No newline at end of file diff --git a/Source/Core/Windows/CustomFieldsForm.Designer.cs b/Source/Core/Windows/CustomFieldsForm.Designer.cs new file mode 100644 index 0000000..24f5d21 --- /dev/null +++ b/Source/Core/Windows/CustomFieldsForm.Designer.cs @@ -0,0 +1,105 @@ +namespace CodeImp.DoomBuilder.Windows +{ + partial class CustomFieldsForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.fieldslist = new CodeImp.DoomBuilder.Controls.FieldsEditorControl(); + this.cancel = new System.Windows.Forms.Button(); + this.apply = new System.Windows.Forms.Button(); + this.SuspendLayout(); + // + // fieldslist + // + this.fieldslist.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.fieldslist.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D; + this.fieldslist.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.fieldslist.Location = new System.Drawing.Point(10, 10); + this.fieldslist.Margin = new System.Windows.Forms.Padding(1); + this.fieldslist.Name = "fieldslist"; + this.fieldslist.Size = new System.Drawing.Size(506, 310); + this.fieldslist.TabIndex = 0; + // + // cancel + // + this.cancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.cancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.cancel.Location = new System.Drawing.Point(404, 336); + this.cancel.Margin = new System.Windows.Forms.Padding(1); + this.cancel.Name = "cancel"; + this.cancel.Size = new System.Drawing.Size(112, 25); + this.cancel.TabIndex = 2; + this.cancel.Text = "Cancel"; + this.cancel.UseVisualStyleBackColor = true; + this.cancel.Click += new System.EventHandler(this.cancel_Click); + // + // apply + // + this.apply.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.apply.Location = new System.Drawing.Point(285, 336); + this.apply.Margin = new System.Windows.Forms.Padding(1); + this.apply.Name = "apply"; + this.apply.Size = new System.Drawing.Size(112, 25); + this.apply.TabIndex = 1; + this.apply.Text = "OK"; + this.apply.UseVisualStyleBackColor = true; + this.apply.Click += new System.EventHandler(this.apply_Click); + // + // CustomFieldsForm + // + this.AcceptButton = this.apply; + this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; + this.CancelButton = this.cancel; + this.ClientSize = new System.Drawing.Size(526, 371); + this.Controls.Add(this.cancel); + this.Controls.Add(this.apply); + this.Controls.Add(this.fieldslist); + this.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "CustomFieldsForm"; + this.Opacity = 0; + this.ShowIcon = false; + this.ShowInTaskbar = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "CustomFieldsForm"; + this.HelpRequested += new System.Windows.Forms.HelpEventHandler(this.CustomFieldsForm_HelpRequested); + this.ResumeLayout(false); + + } + + #endregion + + private CodeImp.DoomBuilder.Controls.FieldsEditorControl fieldslist; + private System.Windows.Forms.Button cancel; + private System.Windows.Forms.Button apply; + } +} \ No newline at end of file diff --git a/Source/Core/Windows/CustomFieldsForm.cs b/Source/Core/Windows/CustomFieldsForm.cs new file mode 100644 index 0000000..b3768ff --- /dev/null +++ b/Source/Core/Windows/CustomFieldsForm.cs @@ -0,0 +1,113 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Text; +using System.Windows.Forms; +using Microsoft.Win32; +using System.Diagnostics; +using CodeImp.DoomBuilder.Actions; +using CodeImp.DoomBuilder.Data; +using CodeImp.DoomBuilder.Config; +using System.IO; +using CodeImp.DoomBuilder.Controls; +using CodeImp.DoomBuilder.Map; + +#endregion + +namespace CodeImp.DoomBuilder.Windows +{ + /// + /// Dialog window that allows you to view and/or change custom UDMF fields. + /// + public partial class CustomFieldsForm : DelayedForm + { + // Keep a list of elements + private ICollection elements; + + // Constructor + public CustomFieldsForm() + { + // Initialize + InitializeComponent(); + } + + // This shows the dialog, returns false when cancelled + public static bool ShowDialog(IWin32Window owner, string title, string elementname, ICollection elements, List fixedfields) + { + bool result; + CustomFieldsForm f = new CustomFieldsForm(); + f.Setup(title, elementname, elements, fixedfields); + result = (f.ShowDialog(owner) == DialogResult.OK); + f.Dispose(); + return result; + } + + // This sets up the dialog + public void Setup(string title, string elementname, ICollection elements, List fixedfields) + { + // Initialize + this.elements = elements; + this.Text = title; + + // Fill universal fields list + fieldslist.ListFixedFields(fixedfields); + + // Initialize custom fields editor + fieldslist.Setup(elementname); + + // Setup from first element + MapElement fe = General.GetByIndex(elements, 0); + fieldslist.SetValues(fe.Fields, true); + + // Setup from all elements + foreach (MapElement e in elements) + fieldslist.SetValues(e.Fields, false); + } + + // OK clicked + private void apply_Click(object sender, EventArgs e) + { + // Apply fields to all elements + foreach (MapElement el in elements) fieldslist.Apply(el.Fields); + + // Done + General.Map.IsChanged = true; + this.DialogResult = DialogResult.OK; + this.Close(); + } + + // Cancel clicked + private void cancel_Click(object sender, EventArgs e) + { + // Be gone + this.DialogResult = DialogResult.Cancel; + this.Close(); + } + + // Help requested + private void CustomFieldsForm_HelpRequested(object sender, HelpEventArgs hlpevent) + { + General.ShowHelp("w_customfields.html"); + hlpevent.Handled = true; + } + } +} diff --git a/Source/Core/Windows/CustomFieldsForm.resx b/Source/Core/Windows/CustomFieldsForm.resx new file mode 100644 index 0000000..135a1bb --- /dev/null +++ b/Source/Core/Windows/CustomFieldsForm.resx @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + True + + + True + + + True + + + True + + \ No newline at end of file diff --git a/Source/Core/Windows/DelayedForm.cs b/Source/Core/Windows/DelayedForm.cs new file mode 100644 index 0000000..8ce0064 --- /dev/null +++ b/Source/Core/Windows/DelayedForm.cs @@ -0,0 +1,81 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Text; +using System.Windows.Forms; +using CodeImp.DoomBuilder.Controls; + +#endregion + +// This Form is a workaround for the slow drawing of the .NET Forms. +// By showing the Form at 0% Opacity it allows the .NET framework to complete +// drawing the Form first, then we set it to 100% Opacity to actually show it. +// To use this class properly, set the initial Opacity of your Form to 0. + +namespace CodeImp.DoomBuilder.Windows +{ + public class DelayedForm : Form + { + // Variables + private Timer formshowtimer; + + // Constructor + public DelayedForm() + { + // Create a timer that we need to show the form + formshowtimer = new Timer(); + formshowtimer.Interval = 1; + formshowtimer.Tick += new EventHandler(formshowtimer_Tick); + } + + // When form is shown + protected override void OnShown(EventArgs e) + { + // Let the base class know + base.OnShown(e); + + // Start the timer to show the form + formshowtimer.Enabled = true; + } + + // When the form is to be shown + private void formshowtimer_Tick(object sender, EventArgs e) + { + // Get rid of the timer + formshowtimer.Dispose(); + formshowtimer = null; + + if (!this.IsDisposed) + { + // Make the form visible + this.Opacity = 100; + } + } + + // Block this + protected override bool ProcessCmdKey(ref Message msg, Keys keyData) + { + //return base.ProcessCmdKey(ref msg, keyData); + return false; + } + } +} diff --git a/Source/Core/Windows/DelayedForm.resx b/Source/Core/Windows/DelayedForm.resx new file mode 100644 index 0000000..19dc0dd --- /dev/null +++ b/Source/Core/Windows/DelayedForm.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/Source/Core/Windows/EffectBrowserForm.Designer.cs b/Source/Core/Windows/EffectBrowserForm.Designer.cs new file mode 100644 index 0000000..0a3b973 --- /dev/null +++ b/Source/Core/Windows/EffectBrowserForm.Designer.cs @@ -0,0 +1,396 @@ +namespace CodeImp.DoomBuilder.Windows +{ + partial class EffectBrowserForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.Windows.Forms.GroupBox groupBox2; + this.option7 = new System.Windows.Forms.ComboBox(); + this.option7label = new System.Windows.Forms.Label(); + this.option6 = new System.Windows.Forms.ComboBox(); + this.option6label = new System.Windows.Forms.Label(); + this.option5 = new System.Windows.Forms.ComboBox(); + this.option5label = new System.Windows.Forms.Label(); + this.option4 = new System.Windows.Forms.ComboBox(); + this.option4label = new System.Windows.Forms.Label(); + this.option3 = new System.Windows.Forms.ComboBox(); + this.option3label = new System.Windows.Forms.Label(); + this.option2 = new System.Windows.Forms.ComboBox(); + this.option2label = new System.Windows.Forms.Label(); + this.option1 = new System.Windows.Forms.ComboBox(); + this.option1label = new System.Windows.Forms.Label(); + this.option0 = new System.Windows.Forms.ComboBox(); + this.option0label = new System.Windows.Forms.Label(); + this.cancel = new System.Windows.Forms.Button(); + this.apply = new System.Windows.Forms.Button(); + this.tabs = new System.Windows.Forms.TabControl(); + this.tabeffects = new System.Windows.Forms.TabPage(); + this.effects = new System.Windows.Forms.ListView(); + this.colnumber = new System.Windows.Forms.ColumnHeader(); + this.colname = new System.Windows.Forms.ColumnHeader(); + this.tabgeneralized = new System.Windows.Forms.TabPage(); + groupBox2 = new System.Windows.Forms.GroupBox(); + groupBox2.SuspendLayout(); + this.tabs.SuspendLayout(); + this.tabeffects.SuspendLayout(); + this.tabgeneralized.SuspendLayout(); + this.SuspendLayout(); + // + // groupBox2 + // + groupBox2.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + groupBox2.Controls.Add(this.option7); + groupBox2.Controls.Add(this.option7label); + groupBox2.Controls.Add(this.option6); + groupBox2.Controls.Add(this.option6label); + groupBox2.Controls.Add(this.option5); + groupBox2.Controls.Add(this.option5label); + groupBox2.Controls.Add(this.option4); + groupBox2.Controls.Add(this.option4label); + groupBox2.Controls.Add(this.option3); + groupBox2.Controls.Add(this.option3label); + groupBox2.Controls.Add(this.option2); + groupBox2.Controls.Add(this.option2label); + groupBox2.Controls.Add(this.option1); + groupBox2.Controls.Add(this.option1label); + groupBox2.Controls.Add(this.option0); + groupBox2.Controls.Add(this.option0label); + groupBox2.Location = new System.Drawing.Point(6, 6); + groupBox2.Name = "groupBox2"; + groupBox2.Size = new System.Drawing.Size(379, 397); + groupBox2.TabIndex = 1; + groupBox2.TabStop = false; + groupBox2.Text = " Options "; + // + // option7 + // + this.option7.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.option7.FormattingEnabled = true; + this.option7.Location = new System.Drawing.Point(118, 280); + this.option7.Name = "option7"; + this.option7.Size = new System.Drawing.Size(199, 22); + this.option7.TabIndex = 7; + this.option7.Visible = false; + // + // option7label + // + this.option7label.Location = new System.Drawing.Point(3, 283); + this.option7label.Name = "option7label"; + this.option7label.Size = new System.Drawing.Size(109, 19); + this.option7label.TabIndex = 16; + this.option7label.Text = "Option:"; + this.option7label.TextAlign = System.Drawing.ContentAlignment.TopRight; + this.option7label.Visible = false; + // + // option6 + // + this.option6.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.option6.FormattingEnabled = true; + this.option6.Location = new System.Drawing.Point(118, 244); + this.option6.Name = "option6"; + this.option6.Size = new System.Drawing.Size(199, 22); + this.option6.TabIndex = 6; + this.option6.Visible = false; + // + // option6label + // + this.option6label.Location = new System.Drawing.Point(3, 247); + this.option6label.Name = "option6label"; + this.option6label.Size = new System.Drawing.Size(109, 19); + this.option6label.TabIndex = 14; + this.option6label.Text = "Option:"; + this.option6label.TextAlign = System.Drawing.ContentAlignment.TopRight; + this.option6label.Visible = false; + // + // option5 + // + this.option5.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.option5.FormattingEnabled = true; + this.option5.Location = new System.Drawing.Point(118, 208); + this.option5.Name = "option5"; + this.option5.Size = new System.Drawing.Size(199, 22); + this.option5.TabIndex = 5; + this.option5.Visible = false; + // + // option5label + // + this.option5label.Location = new System.Drawing.Point(3, 211); + this.option5label.Name = "option5label"; + this.option5label.Size = new System.Drawing.Size(109, 19); + this.option5label.TabIndex = 12; + this.option5label.Text = "Option:"; + this.option5label.TextAlign = System.Drawing.ContentAlignment.TopRight; + this.option5label.Visible = false; + // + // option4 + // + this.option4.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.option4.FormattingEnabled = true; + this.option4.Location = new System.Drawing.Point(118, 172); + this.option4.Name = "option4"; + this.option4.Size = new System.Drawing.Size(199, 22); + this.option4.TabIndex = 4; + this.option4.Visible = false; + // + // option4label + // + this.option4label.Location = new System.Drawing.Point(3, 175); + this.option4label.Name = "option4label"; + this.option4label.Size = new System.Drawing.Size(109, 19); + this.option4label.TabIndex = 10; + this.option4label.Text = "Option:"; + this.option4label.TextAlign = System.Drawing.ContentAlignment.TopRight; + this.option4label.Visible = false; + // + // option3 + // + this.option3.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.option3.FormattingEnabled = true; + this.option3.Location = new System.Drawing.Point(118, 136); + this.option3.Name = "option3"; + this.option3.Size = new System.Drawing.Size(199, 22); + this.option3.TabIndex = 3; + this.option3.Visible = false; + // + // option3label + // + this.option3label.Location = new System.Drawing.Point(3, 139); + this.option3label.Name = "option3label"; + this.option3label.Size = new System.Drawing.Size(109, 19); + this.option3label.TabIndex = 8; + this.option3label.Text = "Option:"; + this.option3label.TextAlign = System.Drawing.ContentAlignment.TopRight; + this.option3label.Visible = false; + // + // option2 + // + this.option2.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.option2.FormattingEnabled = true; + this.option2.Location = new System.Drawing.Point(118, 100); + this.option2.Name = "option2"; + this.option2.Size = new System.Drawing.Size(199, 22); + this.option2.TabIndex = 2; + this.option2.Visible = false; + // + // option2label + // + this.option2label.Location = new System.Drawing.Point(3, 103); + this.option2label.Name = "option2label"; + this.option2label.Size = new System.Drawing.Size(109, 19); + this.option2label.TabIndex = 6; + this.option2label.Text = "Option:"; + this.option2label.TextAlign = System.Drawing.ContentAlignment.TopRight; + this.option2label.Visible = false; + // + // option1 + // + this.option1.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.option1.FormattingEnabled = true; + this.option1.Location = new System.Drawing.Point(118, 64); + this.option1.Name = "option1"; + this.option1.Size = new System.Drawing.Size(199, 22); + this.option1.TabIndex = 1; + this.option1.Visible = false; + // + // option1label + // + this.option1label.Location = new System.Drawing.Point(3, 67); + this.option1label.Name = "option1label"; + this.option1label.Size = new System.Drawing.Size(109, 19); + this.option1label.TabIndex = 4; + this.option1label.Text = "Option:"; + this.option1label.TextAlign = System.Drawing.ContentAlignment.TopRight; + this.option1label.Visible = false; + // + // option0 + // + this.option0.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.option0.FormattingEnabled = true; + this.option0.Location = new System.Drawing.Point(118, 28); + this.option0.Name = "option0"; + this.option0.Size = new System.Drawing.Size(199, 22); + this.option0.TabIndex = 0; + this.option0.Visible = false; + // + // option0label + // + this.option0label.Location = new System.Drawing.Point(3, 31); + this.option0label.Name = "option0label"; + this.option0label.Size = new System.Drawing.Size(109, 19); + this.option0label.TabIndex = 2; + this.option0label.Text = "Option:"; + this.option0label.TextAlign = System.Drawing.ContentAlignment.TopRight; + this.option0label.Visible = false; + // + // cancel + // + this.cancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.cancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.cancel.Location = new System.Drawing.Point(297, 459); + this.cancel.Name = "cancel"; + this.cancel.Size = new System.Drawing.Size(112, 27); + this.cancel.TabIndex = 2; + this.cancel.Text = "Cancel"; + this.cancel.UseVisualStyleBackColor = true; + this.cancel.Click += new System.EventHandler(this.cancel_Click); + // + // apply + // + this.apply.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.apply.Location = new System.Drawing.Point(177, 459); + this.apply.Name = "apply"; + this.apply.Size = new System.Drawing.Size(112, 27); + this.apply.TabIndex = 1; + this.apply.Text = "OK"; + this.apply.UseVisualStyleBackColor = true; + this.apply.Click += new System.EventHandler(this.apply_Click); + // + // tabs + // + this.tabs.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.tabs.Controls.Add(this.tabeffects); + this.tabs.Controls.Add(this.tabgeneralized); + this.tabs.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tabs.ItemSize = new System.Drawing.Size(150, 19); + this.tabs.Location = new System.Drawing.Point(10, 10); + this.tabs.Margin = new System.Windows.Forms.Padding(1); + this.tabs.Name = "tabs"; + this.tabs.SelectedIndex = 0; + this.tabs.Size = new System.Drawing.Size(399, 436); + this.tabs.SizeMode = System.Windows.Forms.TabSizeMode.Fixed; + this.tabs.TabIndex = 0; + // + // tabeffects + // + this.tabeffects.Controls.Add(this.effects); + this.tabeffects.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tabeffects.Location = new System.Drawing.Point(4, 23); + this.tabeffects.Name = "tabeffects"; + this.tabeffects.Padding = new System.Windows.Forms.Padding(3); + this.tabeffects.Size = new System.Drawing.Size(391, 409); + this.tabeffects.TabIndex = 0; + this.tabeffects.Text = "Predefined Effects"; + this.tabeffects.UseVisualStyleBackColor = true; + // + // effects + // + this.effects.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.colnumber, + this.colname}); + this.effects.FullRowSelect = true; + this.effects.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.Nonclickable; + this.effects.HideSelection = false; + this.effects.Location = new System.Drawing.Point(6, 6); + this.effects.MultiSelect = false; + this.effects.Name = "effects"; + this.effects.Size = new System.Drawing.Size(379, 397); + this.effects.TabIndex = 0; + this.effects.UseCompatibleStateImageBehavior = false; + this.effects.View = System.Windows.Forms.View.Details; + this.effects.DoubleClick += new System.EventHandler(this.effects_DoubleClick); + // + // colnumber + // + this.colnumber.Text = "Effect"; + this.colnumber.Width = 70; + // + // colname + // + this.colname.Text = "Description"; + this.colname.Width = 280; + // + // tabgeneralized + // + this.tabgeneralized.Controls.Add(groupBox2); + this.tabgeneralized.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tabgeneralized.Location = new System.Drawing.Point(4, 23); + this.tabgeneralized.Name = "tabgeneralized"; + this.tabgeneralized.Padding = new System.Windows.Forms.Padding(3); + this.tabgeneralized.Size = new System.Drawing.Size(391, 409); + this.tabgeneralized.TabIndex = 1; + this.tabgeneralized.Text = "Generalized Effects"; + this.tabgeneralized.UseVisualStyleBackColor = true; + // + // EffectBrowserForm + // + this.AcceptButton = this.apply; + this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; + this.CancelButton = this.cancel; + this.ClientSize = new System.Drawing.Size(419, 496); + this.Controls.Add(this.tabs); + this.Controls.Add(this.cancel); + this.Controls.Add(this.apply); + this.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "EffectBrowserForm"; + this.Opacity = 0; + this.ShowInTaskbar = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Edit Effect"; + groupBox2.ResumeLayout(false); + this.tabs.ResumeLayout(false); + this.tabeffects.ResumeLayout(false); + this.tabgeneralized.ResumeLayout(false); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.Button cancel; + private System.Windows.Forms.Button apply; + private System.Windows.Forms.TabControl tabs; + private System.Windows.Forms.TabPage tabeffects; + private System.Windows.Forms.TabPage tabgeneralized; + private System.Windows.Forms.ComboBox option7; + private System.Windows.Forms.Label option7label; + private System.Windows.Forms.ComboBox option6; + private System.Windows.Forms.Label option6label; + private System.Windows.Forms.ComboBox option5; + private System.Windows.Forms.Label option5label; + private System.Windows.Forms.ComboBox option4; + private System.Windows.Forms.Label option4label; + private System.Windows.Forms.ComboBox option3; + private System.Windows.Forms.Label option3label; + private System.Windows.Forms.ComboBox option2; + private System.Windows.Forms.Label option2label; + private System.Windows.Forms.ComboBox option1; + private System.Windows.Forms.Label option1label; + private System.Windows.Forms.ComboBox option0; + private System.Windows.Forms.Label option0label; + private System.Windows.Forms.ListView effects; + private System.Windows.Forms.ColumnHeader colnumber; + private System.Windows.Forms.ColumnHeader colname; + } +} \ No newline at end of file diff --git a/Source/Core/Windows/EffectBrowserForm.cs b/Source/Core/Windows/EffectBrowserForm.cs new file mode 100644 index 0000000..c92e489 --- /dev/null +++ b/Source/Core/Windows/EffectBrowserForm.cs @@ -0,0 +1,193 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Text; +using System.Windows.Forms; +using Microsoft.Win32; +using System.Diagnostics; +using CodeImp.DoomBuilder.Actions; +using CodeImp.DoomBuilder.Data; +using CodeImp.DoomBuilder.Config; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Controls; + +#endregion + +namespace CodeImp.DoomBuilder.Windows +{ + internal partial class EffectBrowserForm : DelayedForm + { + // Constants + private const int MAX_OPTIONS = 8; + + // Variables + private int selectedeffect; + private ComboBox[] options; + private Label[] optionlbls; + + // Properties + public int SelectedEffect { get { return selectedeffect; } } + + // Constructor + public EffectBrowserForm(int effect) + { + GeneralizedOption o; + ListViewItem n; + bool selected = false; + + // Initialize + InitializeComponent(); + + // Make array references for controls + options = new ComboBox[] { option0, option1, option2, option3, option4, option5, option6, option7 }; + optionlbls = new Label[] { option0label, option1label, option2label, option3label, option4label, + option5label, option6label, option7label }; + + // Go for all predefined effects + foreach (SectorEffectInfo si in General.Map.Config.SortedSectorEffects) + { + // Create effect + n = effects.Items.Add(si.Index.ToString()); + n.SubItems.Add(si.Title); + n.Tag = si; + if (si.Index == effect) + { + selected = true; + n.Selected = true; + } + } + + // Using generalized effects? + if (General.Map.Config.GeneralizedEffects) + { + // Go for all options + for (int i = 0; i < MAX_OPTIONS; i++) + { + // Option used in selected category? + if (i < General.Map.Config.GenEffectOptions.Count) + { + o = General.Map.Config.GenEffectOptions[i]; + + // Setup controls + optionlbls[i].Text = o.Name + ":"; + options[i].Items.Clear(); + options[i].Items.AddRange(o.Bits.ToArray()); + + // Show option + options[i].Visible = true; + optionlbls[i].Visible = true; + + if (effects.SelectedItems.Count == 0) + { + // Go for all bits + foreach (GeneralizedBit ab in o.Bits) + { + // Select this setting if matches + if ((effect & ab.Index) == ab.Index) options[i].SelectedItem = ab; + } + } + } + else + { + // Hide option + options[i].Visible = false; + optionlbls[i].Visible = false; + } + } + + // Open the generalized tab when given effect is generalized + if (!selected) tabs.SelectedTab = tabgeneralized; + } + else + { + // Remove generalized tab + tabs.TabPages.Remove(tabgeneralized); + } + } + + // This browses for an effect + // Returns the new effect or the same effect when cancelled + public static int BrowseEffect(IWin32Window owner, int effect) + { + EffectBrowserForm f = new EffectBrowserForm(effect); + if (f.ShowDialog(owner) == DialogResult.OK) effect = f.SelectedEffect; + f.Dispose(); + return effect; + } + + // OK clicked + private void apply_Click(object sender, EventArgs e) + { + // Presume no result + selectedeffect = 0; + + // Predefined action? + if (tabs.SelectedTab == tabeffects) + { + // Effect selected? + if ((effects.SelectedItems.Count > 0) && (effects.SelectedItems[0].Tag is SectorEffectInfo)) + { + // Our result + selectedeffect = (effects.SelectedItems[0].Tag as SectorEffectInfo).Index; + } + } + // Generalized action + else + { + // Go for all options + for (int i = 0; i < MAX_OPTIONS; i++) + { + // Option used? + if (i < General.Map.Config.GenEffectOptions.Count) + { + // Add selected bits + if (options[i].SelectedIndex > -1) + selectedeffect += (options[i].SelectedItem as GeneralizedBit).Index; + } + } + } + + // Done + this.DialogResult = DialogResult.OK; + this.Close(); + } + + // Cancel clicked + private void cancel_Click(object sender, EventArgs e) + { + // Leave + this.DialogResult = DialogResult.Cancel; + this.Close(); + } + + // Double-clicked on item + private void effects_DoubleClick(object sender, EventArgs e) + { + // Effect selected? + if ((effects.SelectedItems.Count > 0) && (effects.SelectedItems[0].Tag is SectorEffectInfo)) + { + if (apply.Enabled) apply_Click(this, EventArgs.Empty); + } + } + } +} \ No newline at end of file diff --git a/Source/Core/Windows/EffectBrowserForm.resx b/Source/Core/Windows/EffectBrowserForm.resx new file mode 100644 index 0000000..2b2acc8 --- /dev/null +++ b/Source/Core/Windows/EffectBrowserForm.resx @@ -0,0 +1,195 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + True + + + False + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + \ No newline at end of file diff --git a/Source/Core/Windows/ErrorsForm.Designer.cs b/Source/Core/Windows/ErrorsForm.Designer.cs new file mode 100644 index 0000000..84b2f5d --- /dev/null +++ b/Source/Core/Windows/ErrorsForm.Designer.cs @@ -0,0 +1,185 @@ +namespace CodeImp.DoomBuilder.Windows +{ + partial class ErrorsForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle1 = new System.Windows.Forms.DataGridViewCellStyle(); + this.copyselected = new System.Windows.Forms.Button(); + this.clearlist = new System.Windows.Forms.Button(); + this.close = new System.Windows.Forms.Button(); + this.checkerrors = new System.Windows.Forms.Timer(this.components); + this.checkshow = new System.Windows.Forms.CheckBox(); + this.grid = new System.Windows.Forms.DataGridView(); + this.iconcolumn = new System.Windows.Forms.DataGridViewImageColumn(); + this.textcolumn = new System.Windows.Forms.DataGridViewTextBoxColumn(); + ((System.ComponentModel.ISupportInitialize)(this.grid)).BeginInit(); + this.SuspendLayout(); + // + // copyselected + // + this.copyselected.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.copyselected.Location = new System.Drawing.Point(12, 416); + this.copyselected.Name = "copyselected"; + this.copyselected.Size = new System.Drawing.Size(122, 25); + this.copyselected.TabIndex = 1; + this.copyselected.Text = "Copy Selection"; + this.copyselected.UseVisualStyleBackColor = true; + this.copyselected.Click += new System.EventHandler(this.copyselected_Click); + // + // clearlist + // + this.clearlist.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.clearlist.Location = new System.Drawing.Point(150, 416); + this.clearlist.Name = "clearlist"; + this.clearlist.Size = new System.Drawing.Size(122, 25); + this.clearlist.TabIndex = 2; + this.clearlist.Text = "Clear"; + this.clearlist.UseVisualStyleBackColor = true; + this.clearlist.Click += new System.EventHandler(this.clearlist_Click); + // + // close + // + this.close.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.close.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.close.Location = new System.Drawing.Point(622, 416); + this.close.Name = "close"; + this.close.Size = new System.Drawing.Size(122, 25); + this.close.TabIndex = 4; + this.close.Text = "Close"; + this.close.UseVisualStyleBackColor = true; + this.close.Click += new System.EventHandler(this.close_Click); + // + // checkerrors + // + this.checkerrors.Interval = 1000; + this.checkerrors.Tick += new System.EventHandler(this.checkerrors_Tick); + // + // checkshow + // + this.checkshow.AutoSize = true; + this.checkshow.Location = new System.Drawing.Point(301, 420); + this.checkshow.Name = "checkshow"; + this.checkshow.Size = new System.Drawing.Size(213, 18); + this.checkshow.TabIndex = 3; + this.checkshow.Text = "Show this window when errors occur"; + this.checkshow.UseVisualStyleBackColor = true; + // + // grid + // + this.grid.AllowUserToAddRows = false; + this.grid.AllowUserToDeleteRows = false; + this.grid.AllowUserToResizeColumns = false; + this.grid.AllowUserToResizeRows = false; + this.grid.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.grid.AutoSizeColumnsMode = System.Windows.Forms.DataGridViewAutoSizeColumnsMode.Fill; + this.grid.AutoSizeRowsMode = System.Windows.Forms.DataGridViewAutoSizeRowsMode.AllCells; + this.grid.BackgroundColor = System.Drawing.SystemColors.Window; + this.grid.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D; + this.grid.CellBorderStyle = System.Windows.Forms.DataGridViewCellBorderStyle.None; + this.grid.ClipboardCopyMode = System.Windows.Forms.DataGridViewClipboardCopyMode.EnableWithoutHeaderText; + this.grid.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize; + this.grid.ColumnHeadersVisible = false; + this.grid.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] { + this.iconcolumn, + this.textcolumn}); + this.grid.EditMode = System.Windows.Forms.DataGridViewEditMode.EditProgrammatically; + this.grid.Location = new System.Drawing.Point(12, 12); + this.grid.Name = "grid"; + this.grid.ReadOnly = true; + this.grid.RowHeadersVisible = false; + dataGridViewCellStyle1.Alignment = System.Windows.Forms.DataGridViewContentAlignment.TopLeft; + dataGridViewCellStyle1.Padding = new System.Windows.Forms.Padding(2, 4, 2, 5); + this.grid.RowsDefaultCellStyle = dataGridViewCellStyle1; + this.grid.ScrollBars = System.Windows.Forms.ScrollBars.Vertical; + this.grid.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.FullRowSelect; + this.grid.ShowCellErrors = false; + this.grid.ShowCellToolTips = false; + this.grid.ShowEditingIcon = false; + this.grid.ShowRowErrors = false; + this.grid.Size = new System.Drawing.Size(732, 395); + this.grid.StandardTab = true; + this.grid.TabIndex = 5; + // + // iconcolumn + // + this.iconcolumn.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.None; + this.iconcolumn.HeaderText = "Icon"; + this.iconcolumn.MinimumWidth = 20; + this.iconcolumn.Name = "iconcolumn"; + this.iconcolumn.ReadOnly = true; + this.iconcolumn.Resizable = System.Windows.Forms.DataGridViewTriState.False; + this.iconcolumn.Width = 24; + // + // textcolumn + // + this.textcolumn.HeaderText = "Text"; + this.textcolumn.Name = "textcolumn"; + this.textcolumn.ReadOnly = true; + // + // ErrorsForm + // + this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; + this.CancelButton = this.close; + this.ClientSize = new System.Drawing.Size(756, 453); + this.Controls.Add(this.checkshow); + this.Controls.Add(this.close); + this.Controls.Add(this.clearlist); + this.Controls.Add(this.copyselected); + this.Controls.Add(this.grid); + this.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "ErrorsForm"; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; + this.Text = "Errors and Warnings"; + this.Shown += new System.EventHandler(this.ErrorsForm_Shown); + this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.ErrorsForm_FormClosing); + this.HelpRequested += new System.Windows.Forms.HelpEventHandler(this.ErrorsForm_HelpRequested); + ((System.ComponentModel.ISupportInitialize)(this.grid)).EndInit(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Button copyselected; + private System.Windows.Forms.Button clearlist; + private System.Windows.Forms.Button close; + private System.Windows.Forms.Timer checkerrors; + private System.Windows.Forms.CheckBox checkshow; + private System.Windows.Forms.DataGridView grid; + private System.Windows.Forms.DataGridViewImageColumn iconcolumn; + private System.Windows.Forms.DataGridViewTextBoxColumn textcolumn; + } +} \ No newline at end of file diff --git a/Source/Core/Windows/ErrorsForm.cs b/Source/Core/Windows/ErrorsForm.cs new file mode 100644 index 0000000..58be432 --- /dev/null +++ b/Source/Core/Windows/ErrorsForm.cs @@ -0,0 +1,148 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Text; +using System.Windows.Forms; +using Microsoft.Win32; +using System.Diagnostics; +using CodeImp.DoomBuilder.Actions; +using CodeImp.DoomBuilder.Data; +using CodeImp.DoomBuilder.Config; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Controls; + +#endregion + +namespace CodeImp.DoomBuilder.Windows +{ + public partial class ErrorsForm : Form + { + #region ================== Variables + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + public ErrorsForm() + { + InitializeComponent(); + FillList(); + checkerrors.Start(); + checkshow.Checked = General.Settings.ShowErrorsWindow; + } + + #endregion + + #region ================== Methods + + // This sets up the list + private void FillList() + { + // Fill the list with the items we don't have yet + General.ErrorLogger.HasChanged = false; + List errors = General.ErrorLogger.GetErrors(); + int startindex = grid.Rows.Count; + for (int i = startindex; i < errors.Count; i++) + { + ErrorItem e = errors[i]; + Image icon = (e.type == ErrorType.Error) ? Properties.Resources.ErrorLarge : Properties.Resources.WarningLarge; + int index = grid.Rows.Add(); + DataGridViewRow row = grid.Rows[index]; + row.Cells[0].Value = icon; + row.Cells[0].Style.Alignment = DataGridViewContentAlignment.TopCenter; + row.Cells[0].Style.Padding = new Padding(0, 5, 0, 0); + row.Cells[1].Value = e.message; + row.Cells[1].Style.WrapMode = DataGridViewTriState.True; + } + } + + #endregion + + #region ================== Events + + // Close clicked + private void close_Click(object sender, EventArgs e) + { + this.Close(); + } + + // Closing + private void ErrorsForm_FormClosing(object sender, FormClosingEventArgs e) + { + checkerrors.Stop(); + General.Settings.ShowErrorsWindow = checkshow.Checked; + } + + // Checking for more errors + private void checkerrors_Tick(object sender, EventArgs e) + { + // If errors have been added, update the list + if (General.ErrorLogger.HasChanged) + { + FillList(); + } + } + + // This clears all errors + private void clearlist_Click(object sender, EventArgs e) + { + General.ErrorLogger.Clear(); + grid.Rows.Clear(); + } + + // Copy selection + private void copyselected_Click(object sender, EventArgs e) + { + StringBuilder str = new StringBuilder(""); + if (grid.SelectedCells.Count > 0) + { + Clipboard.Clear(); + foreach (DataGridViewCell c in grid.SelectedCells) + { + if (c.ValueType != typeof(Image)) + { + if (str.Length > 0) str.Append("\r\n"); + str.Append(c.Value.ToString()); + } + } + Clipboard.SetText(str.ToString()); + } + } + + // Help requested + private void ErrorsForm_HelpRequested(object sender, HelpEventArgs hlpevent) + { + General.ShowHelp("w_errorsandwarnings.html"); + hlpevent.Handled = true; + } + + #endregion + + private void ErrorsForm_Shown(object sender, EventArgs e) + { + if (grid.Rows.Count > 0) + grid.Rows[0].Selected = false; + + } + } +} \ No newline at end of file diff --git a/Source/Core/Windows/ErrorsForm.resx b/Source/Core/Windows/ErrorsForm.resx new file mode 100644 index 0000000..34f1c10 --- /dev/null +++ b/Source/Core/Windows/ErrorsForm.resx @@ -0,0 +1,147 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + True + + + True + + + True + + + 103, 17 + + + True + + + True + + + True + + + True + + + True + + \ No newline at end of file diff --git a/Source/Core/Windows/FindReplaceOptions.cs b/Source/Core/Windows/FindReplaceOptions.cs new file mode 100644 index 0000000..d93f0db --- /dev/null +++ b/Source/Core/Windows/FindReplaceOptions.cs @@ -0,0 +1,49 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using System.Drawing; +using System.ComponentModel; +using CodeImp.DoomBuilder.Map; +using SlimDX.Direct3D9; +using SlimDX; +using CodeImp.DoomBuilder.Geometry; +using System.Drawing.Imaging; +using CodeImp.DoomBuilder.Data; +using CodeImp.DoomBuilder.Editing; + +#endregion + +namespace CodeImp.DoomBuilder.Windows +{ + internal struct FindReplaceOptions + { + public string FindText; + public bool CaseSensitive; + public bool WholeWord; + public string ReplaceWith; + } +} + diff --git a/Source/Core/Windows/FlatBrowserForm.Designer.cs b/Source/Core/Windows/FlatBrowserForm.Designer.cs new file mode 100644 index 0000000..2ff3413 --- /dev/null +++ b/Source/Core/Windows/FlatBrowserForm.Designer.cs @@ -0,0 +1,166 @@ +namespace CodeImp.DoomBuilder.Windows +{ + partial class FlatBrowserForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(FlatBrowserForm)); + this.cancel = new System.Windows.Forms.Button(); + this.apply = new System.Windows.Forms.Button(); + this.texturesets = new System.Windows.Forms.ListView(); + this.namecolumn = new System.Windows.Forms.ColumnHeader(); + this.countcolumn = new System.Windows.Forms.ColumnHeader(); + this.smallimages = new System.Windows.Forms.ImageList(this.components); + this.browser = new CodeImp.DoomBuilder.Controls.ImageBrowserControl(); + this.SuspendLayout(); + // + // cancel + // + this.cancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.cancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.cancel.Location = new System.Drawing.Point(781, 596); + this.cancel.Name = "cancel"; + this.cancel.Size = new System.Drawing.Size(100, 25); + this.cancel.TabIndex = 3; + this.cancel.TabStop = false; + this.cancel.Text = "Cancel"; + this.cancel.UseVisualStyleBackColor = true; + this.cancel.Click += new System.EventHandler(this.cancel_Click); + // + // apply + // + this.apply.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.apply.Location = new System.Drawing.Point(675, 596); + this.apply.Name = "apply"; + this.apply.Size = new System.Drawing.Size(100, 25); + this.apply.TabIndex = 2; + this.apply.TabStop = false; + this.apply.Text = "OK"; + this.apply.UseVisualStyleBackColor = true; + this.apply.Click += new System.EventHandler(this.apply_Click); + // + // texturesets + // + this.texturesets.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left))); + this.texturesets.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.namecolumn, + this.countcolumn}); + this.texturesets.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.texturesets.FullRowSelect = true; + this.texturesets.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.None; + this.texturesets.HideSelection = false; + this.texturesets.Location = new System.Drawing.Point(12, 9); + this.texturesets.MultiSelect = false; + this.texturesets.Name = "texturesets"; + this.texturesets.Size = new System.Drawing.Size(200, 576); + this.texturesets.SmallImageList = this.smallimages; + this.texturesets.TabIndex = 0; + this.texturesets.TabStop = false; + this.texturesets.UseCompatibleStateImageBehavior = false; + this.texturesets.View = System.Windows.Forms.View.Details; + this.texturesets.SelectedIndexChanged += new System.EventHandler(this.texturesets_SelectedIndexChanged); + // + // namecolumn + // + this.namecolumn.Text = "Name"; + this.namecolumn.Width = 152; + // + // countcolumn + // + this.countcolumn.Text = "Count"; + this.countcolumn.TextAlign = System.Windows.Forms.HorizontalAlignment.Right; + this.countcolumn.Width = 42; + // + // smallimages + // + this.smallimages.ImageStream = ((System.Windows.Forms.ImageListStreamer)(resources.GetObject("smallimages.ImageStream"))); + this.smallimages.TransparentColor = System.Drawing.Color.Transparent; + this.smallimages.Images.SetKeyName(0, "KnownTextureSet2.ico"); + this.smallimages.Images.SetKeyName(1, "AllTextureSet2.ico"); + this.smallimages.Images.SetKeyName(2, "FileTextureSet.ico"); + this.smallimages.Images.SetKeyName(3, "FolderTextureSet.ico"); + this.smallimages.Images.SetKeyName(4, "PK3TextureSet.ico"); + // + // browser + // + this.browser.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.browser.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.browser.HideInputBox = false; + this.browser.LabelText = "Select or enter a flat name:"; + this.browser.Location = new System.Drawing.Point(218, 9); + this.browser.Name = "browser"; + this.browser.PreventSelection = false; + this.browser.Size = new System.Drawing.Size(663, 610); + this.browser.TabIndex = 1; + this.browser.TabStop = false; + this.browser.SelectedItemDoubleClicked += new CodeImp.DoomBuilder.Controls.ImageBrowserControl.SelectedItemDoubleClickDelegate(this.browser_SelectedItemDoubleClicked); + this.browser.SelectedItemChanged += new CodeImp.DoomBuilder.Controls.ImageBrowserControl.SelectedItemChangedDelegate(this.browser_SelectedItemChanged); + // + // FlatBrowserForm + // + this.AcceptButton = this.apply; + this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; + this.CancelButton = this.cancel; + this.ClientSize = new System.Drawing.Size(893, 631); + this.Controls.Add(this.texturesets); + this.Controls.Add(this.cancel); + this.Controls.Add(this.apply); + this.Controls.Add(this.browser); + this.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.MinimizeBox = false; + this.Name = "FlatBrowserForm"; + this.ShowIcon = false; + this.ShowInTaskbar = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.Manual; + this.Text = "Browse Flats"; + this.Load += new System.EventHandler(this.FlatBrowserForm_Load); + this.Shown += new System.EventHandler(this.FlatBrowserForm_Shown); + this.Activated += new System.EventHandler(this.FlatBrowserForm_Activated); + this.Move += new System.EventHandler(this.FlatBrowserForm_Move); + this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.FlatBrowserForm_FormClosing); + this.HelpRequested += new System.Windows.Forms.HelpEventHandler(this.FlatBrowserForm_HelpRequested); + this.ResizeEnd += new System.EventHandler(this.FlatBrowserForm_ResizeEnd); + this.ResumeLayout(false); + + } + + #endregion + + private CodeImp.DoomBuilder.Controls.ImageBrowserControl browser; + private System.Windows.Forms.Button cancel; + private System.Windows.Forms.Button apply; + private System.Windows.Forms.ListView texturesets; + private System.Windows.Forms.ColumnHeader namecolumn; + private System.Windows.Forms.ImageList smallimages; + private System.Windows.Forms.ColumnHeader countcolumn; + } +} \ No newline at end of file diff --git a/Source/Core/Windows/FlatBrowserForm.cs b/Source/Core/Windows/FlatBrowserForm.cs new file mode 100644 index 0000000..4ee75d4 --- /dev/null +++ b/Source/Core/Windows/FlatBrowserForm.cs @@ -0,0 +1,359 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Text; +using System.Windows.Forms; +using CodeImp.DoomBuilder.IO; +using Microsoft.Win32; +using System.Diagnostics; +using CodeImp.DoomBuilder.Actions; +using CodeImp.DoomBuilder.Data; +using CodeImp.DoomBuilder.Config; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Controls; + +#endregion + +namespace CodeImp.DoomBuilder.Windows +{ + internal partial class FlatBrowserForm : Form + { + // Constants + private const int COLUMN_WIDTH_COUNT = 52; + + // Variables + private string selectedname; + private Point lastposition; + private Size lastsize; + private ListViewGroup usedgroup; + private ListViewGroup availgroup; + private ListViewItem selectedset; + private string selecttextureonfill; + + // Properties + public string SelectedName { get { return selectedname; } } + + // Constructor + public FlatBrowserForm(string selecttexture) + { + Cursor.Current = Cursors.WaitCursor; + ListViewItem item; + bool foundselecttexture = false; + long longname = Lump.MakeLongName(selecttexture ?? ""); + + // Initialize + InitializeComponent(); + browser.ApplySettings(); + + // Update the used textures + General.Map.Data.UpdateUsedTextures(); + + // Resize columns to maximize available width + countcolumn.Width = COLUMN_WIDTH_COUNT; + namecolumn.Width = texturesets.ClientRectangle.Width - SystemInformation.VerticalScrollBarWidth - countcolumn.Width - 2; + + // Fill texture sets list with normal texture sets + foreach (IFilledTextureSet ts in General.Map.Data.TextureSets) + { + item = texturesets.Items.Add(ts.Name); + item.Tag = ts; + item.ImageIndex = 0; + item.UseItemStyleForSubItems = false; + item.SubItems.Add(ts.Textures.Count.ToString(), item.ForeColor, + item.BackColor, new Font(item.Font, FontStyle.Regular)); + } + + // Add container-specific texture sets + foreach (ResourceTextureSet ts in General.Map.Data.ResourceTextureSets) + { + item = texturesets.Items.Add(ts.Name); + item.Tag = ts; + item.ImageIndex = 2 + ts.Location.type; + item.UseItemStyleForSubItems = false; + item.SubItems.Add(ts.Textures.Count.ToString(), item.ForeColor, + item.BackColor, new Font(item.Font, FontStyle.Regular)); + } + + // Add All textures set + item = texturesets.Items.Add(General.Map.Data.AllTextureSet.Name); + item.Tag = General.Map.Data.AllTextureSet; + item.ImageIndex = 1; + item.UseItemStyleForSubItems = false; + item.SubItems.Add(General.Map.Data.AllTextureSet.Textures.Count.ToString(), + item.ForeColor, item.BackColor, new Font(item.Font, FontStyle.Regular)); + + // Select the last one that was selected + string selectname = General.Settings.ReadSetting("browserwindow.textureset", ""); + foreach (ListViewItem i in texturesets.Items) + { + if (i.Text == selectname) + { + IFilledTextureSet set = (i.Tag as IFilledTextureSet); + foreach (ImageData img in set.Textures) + { + if (img.LongName == longname) + { + i.Selected = true; + foundselecttexture = true; + break; + } + } + break; + } + } + + // If the selected texture was not found in the last-selected set, try finding it in the other sets + if (!foundselecttexture) + { + foreach (ListViewItem i in texturesets.Items) + { + IFilledTextureSet set = (i.Tag as IFilledTextureSet); + foreach (ImageData img in set.Textures) + { + if (img.LongName == longname) + { + i.Selected = true; + foundselecttexture = true; + break; + } + } + } + } + + // Texture still now found? Then just select the last used set + if (!foundselecttexture) + { + foreach (ListViewItem i in texturesets.Items) + { + if (i.Text == selectname) + { + i.Selected = true; + foundselecttexture = true; + break; + } + } + } + + // WARNING: Some strange behavior of the listview here! + // When you leave this line out, the list becomes very slow. + // Also, this does not change the item selected previously. + texturesets.Items[0].Selected = true; + + // Texture to select when list is filled + selecttextureonfill = selecttexture; + + // Make groups + usedgroup = browser.AddGroup("Used Textures"); + availgroup = browser.AddGroup("Available Textures"); + + // Keep last position and size + lastposition = this.Location; + lastsize = this.Size; + + // Position window from configuration settings + this.SuspendLayout(); + /* + this.Location = new Point(General.Settings.ReadSetting("browserwindow.positionx", this.Location.X), + General.Settings.ReadSetting("browserwindow.positiony", this.Location.Y)); + */ + this.Size = new Size(General.Settings.ReadSetting("browserwindow.sizewidth", this.Size.Width), + General.Settings.ReadSetting("browserwindow.sizeheight", this.Size.Height)); + this.WindowState = (FormWindowState)General.Settings.ReadSetting("browserwindow.windowstate", (int)FormWindowState.Normal); + if (this.WindowState == FormWindowState.Normal) this.StartPosition = FormStartPosition.CenterParent; + this.ResumeLayout(true); + } + + // Selection changed + private void browser_SelectedItemChanged() + { + apply.Enabled = (browser.SelectedItem != null); + } + + // OK clicked + private void apply_Click(object sender, EventArgs e) + { + // Set selected name and close + if (browser.SelectedItem != null) + { + selectedname = browser.SelectedItem.Text; + DialogResult = DialogResult.OK; + } + else + { + selectedname = ""; + DialogResult = DialogResult.Cancel; + } + this.Close(); + } + + // Cancel clicked + private void cancel_Click(object sender, EventArgs e) + { + // No selection, close + selectedname = ""; + DialogResult = DialogResult.Cancel; + this.Close(); + } + + // Activated + private void FlatBrowserForm_Activated(object sender, EventArgs e) + { + // Focus the textbox + browser.FocusTextbox(); + Cursor.Current = Cursors.Default; + } + + // Loading + private void FlatBrowserForm_Load(object sender, EventArgs e) + { + // Normal windowstate? + if (this.WindowState == FormWindowState.Normal) + { + // Keep last position and size + lastposition = this.Location; + lastsize = this.Size; + } + } + + // Resized + private void FlatBrowserForm_ResizeEnd(object sender, EventArgs e) + { + // Normal windowstate? + if (this.WindowState == FormWindowState.Normal) + { + // Keep last position and size + lastposition = this.Location; + lastsize = this.Size; + } + } + + // Moved + private void FlatBrowserForm_Move(object sender, EventArgs e) + { + // Normal windowstate? + if (this.WindowState == FormWindowState.Normal) + { + // Keep last position and size + lastposition = this.Location; + lastsize = this.Size; + } + } + + // Closing + private void FlatBrowserForm_FormClosing(object sender, FormClosingEventArgs e) + { + int windowstate; + + // Determine window state to save + if (this.WindowState != FormWindowState.Minimized) + windowstate = (int)this.WindowState; + else + windowstate = (int)FormWindowState.Normal; + + // Save window settings + General.Settings.WriteSetting("browserwindow.positionx", lastposition.X); + General.Settings.WriteSetting("browserwindow.positiony", lastposition.Y); + General.Settings.WriteSetting("browserwindow.sizewidth", lastsize.Width); + General.Settings.WriteSetting("browserwindow.sizeheight", lastsize.Height); + General.Settings.WriteSetting("browserwindow.windowstate", windowstate); + + // Save last selected texture set + if (texturesets.SelectedItems.Count > 0) + General.Settings.WriteSetting("browserwindow.textureset", texturesets.SelectedItems[0].Text); + + // Clean up + browser.CleanUp(); + } + + // Static method to browse for flats + // Returns null when cancelled. + public static string Browse(IWin32Window parent, string select) + { + FlatBrowserForm browser = new FlatBrowserForm(select); + if (browser.ShowDialog(parent) == DialogResult.OK) + { + // Return result + return browser.SelectedName; + } + else + { + // Cancelled + return select; + } + } + + // Texture set selected + private void texturesets_SelectedIndexChanged(object sender, EventArgs e) + { + // Anything slected? + if (texturesets.SelectedItems.Count > 0) + { + selectedset = texturesets.SelectedItems[0]; + FillImagesList(null); + } + } + + // Item double clicked + private void browser_SelectedItemDoubleClicked() + { + if (apply.Enabled) apply_Click(this, EventArgs.Empty); + } + + // This fills the list of textures, depending on the selected texture set + private void FillImagesList(string selectflat) + { + // Get the selected texture set + IFilledTextureSet set = (selectedset.Tag as IFilledTextureSet); + + // Start adding + browser.BeginAdding(false); + + // Add all used flats + foreach (ImageData img in set.Flats) + if (img.UsedInMap) browser.Add(img.Name, img, img, usedgroup); + + // Add all available flats + foreach (ImageData img in set.Flats) + browser.Add(img.Name, img, img, availgroup); + + // Done adding + browser.EndAdding(); + } + + // Help requested + private void FlatBrowserForm_HelpRequested(object sender, HelpEventArgs hlpevent) + { + General.ShowHelp("w_imagesbrowser.html"); + hlpevent.Handled = true; + } + + private void FlatBrowserForm_Shown(object sender, EventArgs e) + { + // Select texture + if (!string.IsNullOrEmpty(selecttextureonfill)) + { + browser.SelectItem(selecttextureonfill, usedgroup); + selecttextureonfill = null; + } + } + } +} \ No newline at end of file diff --git a/Source/Core/Windows/FlatBrowserForm.resx b/Source/Core/Windows/FlatBrowserForm.resx new file mode 100644 index 0000000..5a00251 --- /dev/null +++ b/Source/Core/Windows/FlatBrowserForm.resx @@ -0,0 +1,199 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + True + + + True + + + True + + + 17, 17 + + + + AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj0yLjAuMC4w + LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAACZTeXN0 + ZW0uV2luZG93cy5Gb3Jtcy5JbWFnZUxpc3RTdHJlYW1lcgEAAAAERGF0YQcCAgAAAAkDAAAADwMAAAB+ + DAAAAk1TRnQBSQFMAgEBBQEAAQkBAAEEAQABEAEAARYBAAT/AQkBAAj/AUIBTQE2AQQGAAE2AQQCAAEo + AwABQAMAASwDAAEBAQABCAYAAQsYAAGAAgABgAMAAoABAAGAAwABgAEAAYABAAKAAgADwAEAAcAB3AHA + AQAB8AHKAaYBAAEzBQABMwEAATMBAAEzAQACMwIAAxYBAAMcAQADIgEAAykBAANVAQADTQEAA0IBAAM5 + AQABgAF8Af8BAAJQAf8BAAGTAQAB1gEAAf8B7AHMAQABxgHWAe8BAAHWAucBAAGQAakBrQIAAf8BMwMA + AWYDAAGZAwABzAIAATMDAAIzAgABMwFmAgABMwGZAgABMwHMAgABMwH/AgABZgMAAWYBMwIAAmYCAAFm + AZkCAAFmAcwCAAFmAf8CAAGZAwABmQEzAgABmQFmAgACmQIAAZkBzAIAAZkB/wIAAcwDAAHMATMCAAHM + AWYCAAHMAZkCAALMAgABzAH/AgAB/wFmAgAB/wGZAgAB/wHMAQABMwH/AgAB/wEAATMBAAEzAQABZgEA + ATMBAAGZAQABMwEAAcwBAAEzAQAB/wEAAf8BMwIAAzMBAAIzAWYBAAIzAZkBAAIzAcwBAAIzAf8BAAEz + AWYCAAEzAWYBMwEAATMCZgEAATMBZgGZAQABMwFmAcwBAAEzAWYB/wEAATMBmQIAATMBmQEzAQABMwGZ + AWYBAAEzApkBAAEzAZkBzAEAATMBmQH/AQABMwHMAgABMwHMATMBAAEzAcwBZgEAATMBzAGZAQABMwLM + AQABMwHMAf8BAAEzAf8BMwEAATMB/wFmAQABMwH/AZkBAAEzAf8BzAEAATMC/wEAAWYDAAFmAQABMwEA + AWYBAAFmAQABZgEAAZkBAAFmAQABzAEAAWYBAAH/AQABZgEzAgABZgIzAQABZgEzAWYBAAFmATMBmQEA + AWYBMwHMAQABZgEzAf8BAAJmAgACZgEzAQADZgEAAmYBmQEAAmYBzAEAAWYBmQIAAWYBmQEzAQABZgGZ + AWYBAAFmApkBAAFmAZkBzAEAAWYBmQH/AQABZgHMAgABZgHMATMBAAFmAcwBmQEAAWYCzAEAAWYBzAH/ + AQABZgH/AgABZgH/ATMBAAFmAf8BmQEAAWYB/wHMAQABzAEAAf8BAAH/AQABzAEAApkCAAGZATMBmQEA + AZkBAAGZAQABmQEAAcwBAAGZAwABmQIzAQABmQEAAWYBAAGZATMBzAEAAZkBAAH/AQABmQFmAgABmQFm + ATMBAAGZATMBZgEAAZkBZgGZAQABmQFmAcwBAAGZATMB/wEAApkBMwEAApkBZgEAA5kBAAKZAcwBAAKZ + Af8BAAGZAcwCAAGZAcwBMwEAAWYBzAFmAQABmQHMAZkBAAGZAswBAAGZAcwB/wEAAZkB/wIAAZkB/wEz + AQABmQHMAWYBAAGZAf8BmQEAAZkB/wHMAQABmQL/AQABzAMAAZkBAAEzAQABzAEAAWYBAAHMAQABmQEA + AcwBAAHMAQABmQEzAgABzAIzAQABzAEzAWYBAAHMATMBmQEAAcwBMwHMAQABzAEzAf8BAAHMAWYCAAHM + AWYBMwEAAZkCZgEAAcwBZgGZAQABzAFmAcwBAAGZAWYB/wEAAcwBmQIAAcwBmQEzAQABzAGZAWYBAAHM + ApkBAAHMAZkBzAEAAcwBmQH/AQACzAIAAswBMwEAAswBZgEAAswBmQEAA8wBAALMAf8BAAHMAf8CAAHM + Af8BMwEAAZkB/wFmAQABzAH/AZkBAAHMAf8BzAEAAcwC/wEAAcwBAAEzAQAB/wEAAWYBAAH/AQABmQEA + AcwBMwIAAf8CMwEAAf8BMwFmAQAB/wEzAZkBAAH/ATMBzAEAAf8BMwH/AQAB/wFmAgAB/wFmATMBAAHM + AmYBAAH/AWYBmQEAAf8BZgHMAQABzAFmAf8BAAH/AZkCAAH/AZkBMwEAAf8BmQFmAQAB/wKZAQAB/wGZ + AcwBAAH/AZkB/wEAAf8BzAIAAf8BzAEzAQAB/wHMAWYBAAH/AcwBmQEAAf8CzAEAAf8BzAH/AQAC/wEz + AQABzAH/AWYBAAL/AZkBAAL/AcwBAAJmAf8BAAFmAf8BZgEAAWYC/wEAAf8CZgEAAf8BZgH/AQAC/wFm + AQABIQEAAaUBAANfAQADdwEAA4YBAAOWAQADywEAA7IBAAPXAQAD3QEAA+MBAAPqAQAD8QEAA/gBAAHw + AfsB/wEAAaQCoAEAA4ADAAH/AgAB/wMAAv8BAAH/AwAB/wEAAf8BAAL/AgAD//8ABQAHDgEQAW0BbAIO + MwABDgENAWwBkAG7AgkB9wJsAWYCDjsAAQ4B6gFsAQ8BDjQAAW0HAAFDAW0BDjQAAW0B8QFtAQcBvAHw + AfECAAERAQcB7DQAA20BBwK8AfACAAJtAQ40AAFtAfADBwK8AgABEQEHAew0AAFtAfAB7wMHAbwCAAJt + AQ40AAFtAfAC7wMHAgABEQEHAew0AAFtAfAD7wIHAgACbQEONAABbQG8BO8BBwIAAREBBwHsNAAIbQEA + AQ8B7AEPNAAJDgHrAZEBDTQAAQ4BZQGQAbsFCQH3AWwCDj4AAQ7/AMMAAfcCrgFtAWwB6gJsBGY0AAH3 + Bf8C9AMZAWYJAAgOEwAB9wKuAW0BbAHqAmwEZgQAAbUC/wKTAZkBBwG7AbQCGQNmBgABDgEABe8B9wEO + AwACcwHrAW4BbQFKARMBFQFDAREBDwEOAQ8DAAH3Bf8C9AMZAWYEAAG1Av8BkwGaAXoBnwGeAbsB9AHz + AWYBGQFmBQABDgHvAQAB8ALxAvIBvAEOAwABcwIcBzECSwFEAe0CAAG1Av8CkwGZAQcBuwG0AhkDZgIA + AbUC/wG7AQgCnwF+AXkC9AFsARkDZgIAAQ4B7wEHAQAB8ALxAvIB8AEOAwAB7AF6ARwBmgN6AlkBUwFS + ATEBSwHqAgABtQL/AZMBmgF6AZ8BngG7AfQB8wFmARkBZgIAAbUC/wLbAQkBCAF+AXkB/wH0AWwB8wFm + ARkBZgIAAQ4CAAHvAbwB8ALxAfIB8AEOAwAB7QF6ARwBmQV6AlkBUwExAUQB7QEAAbUC/wG7AQgCnwF+ + AXkC9AFsARkDZgEHCv8BbAH0AWwBGQFmAgABDgHwAgcCvALwAfEBvAEOAwAB7QGaAXoBHAGaBXoDWQFL + AW0BAAG1Av8C2wEJAQgBfgF5Af8B9AFsAfMBZgEZAWYCBwS1AfcBtQLtApEB9AFsAfMBZgIAAQ4B8AMH + ArwC8AG8AQ4DAAEcAZoBegEcAZkHegFZAVIBSgHtAQcK/wFsAfQBbAEZAWYBtQH/AQcK/wFsAfQBbAIA + AQ4B8AQHArwB8AG8AQ4DAAEcAZoBoAF6ARwHmgJ6AXQBbQIHBLUB9wG1Au0CkQH0AWwB8wFmAbUB/wIH + BLUB9wG1Au0CkQH0AWwCAAEOAfAC7wQHAbwBBwEOAwABHAOgCBwC7QHsAXMCAAEHCv8BbAH0AWwBBwP/ + AQcK/wFsAgABDgHwA+8FBwEOAwABHASgAZoGegFzBQACBwS1AfcBtQLtApEB9AFsAgcCtQIHBLUB9wG1 + Au0CkQIAAQ4BvATvBAcBDgMAARwBoAHDA6ABegMcAu0BcwcAAQcK/wFsAgABBwr/AWwB9AFsAgABDgG8 + Be8DBwEOAwABHAGZAsMCoAEcAe0MAAIHBLUB9wG1Au0CkQIAAgcEtQH3AbUC7QKRAfQBbAIAAQ4B9APw + A7wCBwEOBAAFHAHtHQABBwr/AWwCAAsOJwACBwS1AfcBtQLtApHgAAFCAU0BPgcAAT4DAAEoAwABQAMA + ASwDAAEBAQABAQUAAWABARYAA/8BAAL/BgAC/wYAAv8GAAH/AcMGAAHgAQEGAAHAAQEGAAGAAQEGAAHg + ASMGAAHAASMGAAHAASMGAAHAASMGAAHAASMGAAHAASMGAAHAASMGAAHAASMGAAHAASMGAAGAAQEGAAHA + AQEGAAHgAQMGAAL/BgAC/wYAAv8GABr/AQABDwb/AQABDwH4AQcC/wEAAQ8BAAEDAfABBwEAAQcBAAEP + AQABAwHgAQcBAAEDAQABAwIAAcABBwEAAQMBAAEDAgABwAEHAQABAQQAAcABBwEAAQEEAAHAAQcGAAHA + AQcGAAHAAQcCAAHAAwABwAEHAQABBwHAAwABwAEHAQABBwHwAQABwAEAAcABBwEAAf8B8AEAAcABAAHA + AQcBgQP/AfABAAHAAQcE/wHwAQAc/ws= + + + + True + + + True + + \ No newline at end of file diff --git a/Source/Core/Windows/GridSetupForm.Designer.cs b/Source/Core/Windows/GridSetupForm.Designer.cs new file mode 100644 index 0000000..980bdca --- /dev/null +++ b/Source/Core/Windows/GridSetupForm.Designer.cs @@ -0,0 +1,305 @@ +namespace CodeImp.DoomBuilder.Windows +{ + partial class GridSetupForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.Windows.Forms.GroupBox groupBox1; + System.Windows.Forms.Label label1; + System.Windows.Forms.GroupBox groupBox2; + this.gridsize = new CodeImp.DoomBuilder.Controls.ButtonsNumericTextbox(); + this.backscaley = new CodeImp.DoomBuilder.Controls.ButtonsNumericTextbox(); + this.backscalex = new CodeImp.DoomBuilder.Controls.ButtonsNumericTextbox(); + this.backoffsety = new CodeImp.DoomBuilder.Controls.ButtonsNumericTextbox(); + this.backoffsetx = new CodeImp.DoomBuilder.Controls.ButtonsNumericTextbox(); + this.backscale = new System.Windows.Forms.Label(); + this.selectfile = new System.Windows.Forms.Button(); + this.showbackground = new System.Windows.Forms.CheckBox(); + this.backoffset = new System.Windows.Forms.Label(); + this.selectflat = new System.Windows.Forms.Button(); + this.selecttexture = new System.Windows.Forms.Button(); + this.backgroundimage = new System.Windows.Forms.Panel(); + this.cancel = new System.Windows.Forms.Button(); + this.apply = new System.Windows.Forms.Button(); + this.browsefile = new System.Windows.Forms.OpenFileDialog(); + groupBox1 = new System.Windows.Forms.GroupBox(); + label1 = new System.Windows.Forms.Label(); + groupBox2 = new System.Windows.Forms.GroupBox(); + groupBox1.SuspendLayout(); + groupBox2.SuspendLayout(); + this.SuspendLayout(); + // + // groupBox1 + // + groupBox1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + groupBox1.Controls.Add(this.gridsize); + groupBox1.Controls.Add(label1); + groupBox1.Location = new System.Drawing.Point(12, 12); + groupBox1.Name = "groupBox1"; + groupBox1.Size = new System.Drawing.Size(293, 71); + groupBox1.TabIndex = 0; + groupBox1.TabStop = false; + groupBox1.Text = " Grid "; + // + // gridsize + // + this.gridsize.AllowNegative = false; + this.gridsize.AllowRelative = true; + this.gridsize.ButtonStep = 8; + this.gridsize.Location = new System.Drawing.Point(146, 26); + this.gridsize.Name = "gridsize"; + this.gridsize.Size = new System.Drawing.Size(75, 24); + this.gridsize.TabIndex = 1; + // + // label1 + // + label1.AutoSize = true; + label1.Location = new System.Drawing.Point(25, 31); + label1.Name = "label1"; + label1.Size = new System.Drawing.Size(115, 14); + label1.TabIndex = 0; + label1.Text = "Grid size in mappixels:"; + // + // groupBox2 + // + groupBox2.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + groupBox2.Controls.Add(this.backscaley); + groupBox2.Controls.Add(this.backscalex); + groupBox2.Controls.Add(this.backoffsety); + groupBox2.Controls.Add(this.backoffsetx); + groupBox2.Controls.Add(this.backscale); + groupBox2.Controls.Add(this.selectfile); + groupBox2.Controls.Add(this.showbackground); + groupBox2.Controls.Add(this.backoffset); + groupBox2.Controls.Add(this.selectflat); + groupBox2.Controls.Add(this.selecttexture); + groupBox2.Controls.Add(this.backgroundimage); + groupBox2.Location = new System.Drawing.Point(12, 89); + groupBox2.Name = "groupBox2"; + groupBox2.Size = new System.Drawing.Size(293, 261); + groupBox2.TabIndex = 1; + groupBox2.TabStop = false; + groupBox2.Text = " Background "; + // + // backscaley + // + this.backscaley.AllowNegative = false; + this.backscaley.AllowRelative = true; + this.backscaley.ButtonStep = 1; + this.backscaley.Enabled = false; + this.backscaley.Location = new System.Drawing.Point(197, 212); + this.backscaley.Name = "backscaley"; + this.backscaley.Size = new System.Drawing.Size(67, 24); + this.backscaley.TabIndex = 13; + // + // backscalex + // + this.backscalex.AllowNegative = false; + this.backscalex.AllowRelative = true; + this.backscalex.ButtonStep = 1; + this.backscalex.Enabled = false; + this.backscalex.Location = new System.Drawing.Point(124, 212); + this.backscalex.Name = "backscalex"; + this.backscalex.Size = new System.Drawing.Size(67, 24); + this.backscalex.TabIndex = 12; + // + // backoffsety + // + this.backoffsety.AllowNegative = true; + this.backoffsety.AllowRelative = true; + this.backoffsety.ButtonStep = 1; + this.backoffsety.Enabled = false; + this.backoffsety.Location = new System.Drawing.Point(197, 173); + this.backoffsety.Name = "backoffsety"; + this.backoffsety.Size = new System.Drawing.Size(67, 24); + this.backoffsety.TabIndex = 11; + // + // backoffsetx + // + this.backoffsetx.AllowNegative = true; + this.backoffsetx.AllowRelative = true; + this.backoffsetx.ButtonStep = 1; + this.backoffsetx.Enabled = false; + this.backoffsetx.Location = new System.Drawing.Point(124, 173); + this.backoffsetx.Name = "backoffsetx"; + this.backoffsetx.Size = new System.Drawing.Size(67, 24); + this.backoffsetx.TabIndex = 10; + // + // backscale + // + this.backscale.AutoSize = true; + this.backscale.Enabled = false; + this.backscale.Location = new System.Drawing.Point(30, 217); + this.backscale.Name = "backscale"; + this.backscale.Size = new System.Drawing.Size(88, 14); + this.backscale.TabIndex = 9; + this.backscale.Text = "Scale in percent:"; + // + // selectfile + // + this.selectfile.Enabled = false; + this.selectfile.Location = new System.Drawing.Point(147, 122); + this.selectfile.Name = "selectfile"; + this.selectfile.Size = new System.Drawing.Size(117, 25); + this.selectfile.TabIndex = 3; + this.selectfile.Text = "Select File..."; + this.selectfile.UseVisualStyleBackColor = true; + this.selectfile.Click += new System.EventHandler(this.selectfile_Click); + // + // showbackground + // + this.showbackground.AutoSize = true; + this.showbackground.Location = new System.Drawing.Point(28, 29); + this.showbackground.Name = "showbackground"; + this.showbackground.Size = new System.Drawing.Size(146, 18); + this.showbackground.TabIndex = 0; + this.showbackground.Text = "Show background image"; + this.showbackground.UseVisualStyleBackColor = true; + this.showbackground.CheckedChanged += new System.EventHandler(this.showbackground_CheckedChanged); + // + // backoffset + // + this.backoffset.AutoSize = true; + this.backoffset.Enabled = false; + this.backoffset.Location = new System.Drawing.Point(15, 178); + this.backoffset.Name = "backoffset"; + this.backoffset.Size = new System.Drawing.Size(103, 14); + this.backoffset.TabIndex = 4; + this.backoffset.Text = "Offset in mappixels:"; + // + // selectflat + // + this.selectflat.Enabled = false; + this.selectflat.Location = new System.Drawing.Point(147, 91); + this.selectflat.Name = "selectflat"; + this.selectflat.Size = new System.Drawing.Size(117, 25); + this.selectflat.TabIndex = 2; + this.selectflat.Text = "Select Flat..."; + this.selectflat.UseVisualStyleBackColor = true; + this.selectflat.Click += new System.EventHandler(this.selectflat_Click); + // + // selecttexture + // + this.selecttexture.Enabled = false; + this.selecttexture.Location = new System.Drawing.Point(147, 60); + this.selecttexture.Name = "selecttexture"; + this.selecttexture.Size = new System.Drawing.Size(117, 25); + this.selecttexture.TabIndex = 1; + this.selecttexture.Text = "Select Texture..."; + this.selecttexture.UseVisualStyleBackColor = true; + this.selecttexture.Click += new System.EventHandler(this.selecttexture_Click); + // + // backgroundimage + // + this.backgroundimage.BackColor = System.Drawing.SystemColors.AppWorkspace; + this.backgroundimage.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Zoom; + this.backgroundimage.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D; + this.backgroundimage.Location = new System.Drawing.Point(28, 60); + this.backgroundimage.Name = "backgroundimage"; + this.backgroundimage.Size = new System.Drawing.Size(91, 87); + this.backgroundimage.TabIndex = 1; + // + // cancel + // + this.cancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.cancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.cancel.Location = new System.Drawing.Point(193, 368); + this.cancel.Name = "cancel"; + this.cancel.Size = new System.Drawing.Size(112, 25); + this.cancel.TabIndex = 3; + this.cancel.Text = "Cancel"; + this.cancel.UseVisualStyleBackColor = true; + this.cancel.Click += new System.EventHandler(this.cancel_Click); + // + // apply + // + this.apply.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.apply.Location = new System.Drawing.Point(75, 368); + this.apply.Name = "apply"; + this.apply.Size = new System.Drawing.Size(112, 25); + this.apply.TabIndex = 2; + this.apply.Text = "OK"; + this.apply.UseVisualStyleBackColor = true; + this.apply.Click += new System.EventHandler(this.apply_Click); + // + // browsefile + // + this.browsefile.Filter = "All supported images|*.bmp;*.gif;*.png|All Files|*.*"; + this.browsefile.RestoreDirectory = true; + this.browsefile.Title = "Select Background Image File"; + // + // GridSetupForm + // + this.AcceptButton = this.apply; + this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; + this.CancelButton = this.cancel; + this.ClientSize = new System.Drawing.Size(317, 403); + this.Controls.Add(this.cancel); + this.Controls.Add(this.apply); + this.Controls.Add(groupBox2); + this.Controls.Add(groupBox1); + this.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "GridSetupForm"; + this.Opacity = 0; + this.ShowIcon = false; + this.ShowInTaskbar = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Custom Grid Setup"; + this.HelpRequested += new System.Windows.Forms.HelpEventHandler(this.GridSetupForm_HelpRequested); + groupBox1.ResumeLayout(false); + groupBox1.PerformLayout(); + groupBox2.ResumeLayout(false); + groupBox2.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.Panel backgroundimage; + private System.Windows.Forms.Button selecttexture; + private System.Windows.Forms.Button selectflat; + private System.Windows.Forms.CheckBox showbackground; + private System.Windows.Forms.Button cancel; + private System.Windows.Forms.Button apply; + private System.Windows.Forms.Label backoffset; + private System.Windows.Forms.Button selectfile; + private System.Windows.Forms.Label backscale; + private System.Windows.Forms.OpenFileDialog browsefile; + private CodeImp.DoomBuilder.Controls.ButtonsNumericTextbox gridsize; + private CodeImp.DoomBuilder.Controls.ButtonsNumericTextbox backscaley; + private CodeImp.DoomBuilder.Controls.ButtonsNumericTextbox backscalex; + private CodeImp.DoomBuilder.Controls.ButtonsNumericTextbox backoffsety; + private CodeImp.DoomBuilder.Controls.ButtonsNumericTextbox backoffsetx; + } +} \ No newline at end of file diff --git a/Source/Core/Windows/GridSetupForm.cs b/Source/Core/Windows/GridSetupForm.cs new file mode 100644 index 0000000..8e4b012 --- /dev/null +++ b/Source/Core/Windows/GridSetupForm.cs @@ -0,0 +1,185 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Text; +using System.Windows.Forms; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Data; +using CodeImp.DoomBuilder.IO; +using System.IO; +using CodeImp.DoomBuilder.Config; +using CodeImp.DoomBuilder.Editing; +using CodeImp.DoomBuilder.Controls; + +#endregion + +namespace CodeImp.DoomBuilder.Windows +{ + internal partial class GridSetupForm : DelayedForm + { + // Variables + private string backgroundname; + private int backgroundsource; + + // Constructor + public GridSetupForm() + { + // Initialize + InitializeComponent(); + + // Show grid size + gridsize.Text = General.Map.Grid.GridSize.ToString(); + + // Background image? + if ((General.Map.Grid.Background != null) && + !(General.Map.Grid.Background is UnknownImage)) + { + // Show background image + showbackground.Checked = true; + backgroundname = General.Map.Grid.BackgroundName; + backgroundsource = General.Map.Grid.BackgroundSource; + General.DisplayZoomedImage(backgroundimage, General.Map.Grid.Background.GetBitmap()); + } + else + { + // No background image + showbackground.Checked = false; + } + + // Show background offset + backoffsetx.Text = General.Map.Grid.BackgroundX.ToString(); + backoffsety.Text = General.Map.Grid.BackgroundY.ToString(); + int scalex = (int)(General.Map.Grid.BackgroundScaleX * 100.0f); + int scaley = (int)(General.Map.Grid.BackgroundScaleY * 100.0f); + backscalex.Text = General.Clamp(scalex, 1, 10000).ToString(); + backscaley.Text = General.Clamp(scaley, 1, 10000).ToString(); + } + + // Show Background changed + private void showbackground_CheckedChanged(object sender, EventArgs e) + { + // Enable/disable controls + selecttexture.Enabled = showbackground.Checked; + selectflat.Enabled = showbackground.Checked; + selectfile.Enabled = showbackground.Checked; + backoffset.Enabled = showbackground.Checked; + backscale.Enabled = showbackground.Checked; + backoffsetx.Enabled = showbackground.Checked; + backoffsety.Enabled = showbackground.Checked; + backscalex.Enabled = showbackground.Checked; + backscaley.Enabled = showbackground.Checked; + } + + // Browse texture + private void selecttexture_Click(object sender, EventArgs e) + { + string result; + + // Browse for texture + result = TextureBrowserForm.Browse(this, backgroundname); + if (result != null) + { + // Set this texture as background + backgroundname = result; + backgroundsource = GridSetup.SOURCE_TEXTURES; + ImageData img = General.Map.Data.GetTextureImage(result); + img.LoadImage(); + General.DisplayZoomedImage(backgroundimage, img.GetBitmap()); + } + } + + // Browse flat + private void selectflat_Click(object sender, EventArgs e) + { + string result; + + // Browse for flat + result = FlatBrowserForm.Browse(this, backgroundname); + if (result != null) + { + // Set this flat as background + backgroundname = result; + backgroundsource = GridSetup.SOURCE_FLATS; + ImageData img = General.Map.Data.GetFlatImage(result); + img.LoadImage(); + General.DisplayZoomedImage(backgroundimage, img.GetBitmap()); + } + } + + // Browse file + private void selectfile_Click(object sender, EventArgs e) + { + // Browse for file + if (browsefile.ShowDialog(this) == DialogResult.OK) + { + // Set this file as background + backgroundname = browsefile.FileName; + backgroundsource = GridSetup.SOURCE_FILE; + ImageData img = new FileImage(backgroundname, backgroundname, false, 1.0f, 1.0f); + img.LoadImage(); + General.DisplayZoomedImage(backgroundimage, new Bitmap(img.GetBitmap())); + img.Dispose(); + } + } + + // Cancelled + private void cancel_Click(object sender, EventArgs e) + { + DialogResult = DialogResult.Cancel; + this.Close(); + } + + // Apply + private void apply_Click(object sender, EventArgs e) + { + // Apply + General.Map.Grid.SetGridSize(gridsize.GetResult(General.Map.Grid.GridSize)); + General.Map.Grid.SetBackgroundView(backoffsetx.GetResult(General.Map.Grid.BackgroundX), + backoffsety.GetResult(General.Map.Grid.BackgroundY), + backscalex.GetResult((int)(General.Map.Grid.BackgroundScaleX * 100.0f)) / 100.0f, + backscaley.GetResult((int)(General.Map.Grid.BackgroundScaleY * 100.0f)) / 100.0f); + + // Background image? + if (showbackground.Checked) + { + // Set background image + General.Map.Grid.SetBackground(backgroundname, backgroundsource); + } + else + { + // No background image + General.Map.Grid.SetBackground(null, 0); + } + + // Done + DialogResult = DialogResult.OK; + this.Close(); + } + + // Help + private void GridSetupForm_HelpRequested(object sender, HelpEventArgs hlpevent) + { + General.ShowHelp("w_gridsetup.html"); + hlpevent.Handled = true; + } + } +} \ No newline at end of file diff --git a/Source/Core/Windows/GridSetupForm.resx b/Source/Core/Windows/GridSetupForm.resx new file mode 100644 index 0000000..8027bc4 --- /dev/null +++ b/Source/Core/Windows/GridSetupForm.resx @@ -0,0 +1,186 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + True + + + False + + + True + + + True + + + False + + + True + + + False + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + 17, 17 + + + True + + \ No newline at end of file diff --git a/Source/Core/Windows/IMainForm.cs b/Source/Core/Windows/IMainForm.cs new file mode 100644 index 0000000..cdfccfc --- /dev/null +++ b/Source/Core/Windows/IMainForm.cs @@ -0,0 +1,172 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using System.Drawing; +using System.ComponentModel; +using CodeImp.DoomBuilder.Actions; +using CodeImp.DoomBuilder.Map; +using SlimDX.Direct3D9; +using SlimDX; +using CodeImp.DoomBuilder.Geometry; +using System.Drawing.Imaging; +using CodeImp.DoomBuilder.Data; +using CodeImp.DoomBuilder.Editing; +using CodeImp.DoomBuilder.Controls; + +#endregion + +namespace CodeImp.DoomBuilder.Windows +{ + public interface IMainForm : IWin32Window + { + // Properties + bool AltState { get; } + bool CtrlState { get; } + bool ShiftState { get; } + bool MouseInDisplay { get; } + bool AutoMerge { get; } + bool SnapToGrid { get; } + bool MouseExclusive { get; } + MouseButtons MouseButtons { get; } + bool IsActiveWindow { get; } + RenderTargetControl Display { get; } + + // Methods + void DisplayReady(); + void DisplayStatus(StatusType type, string message); + void DisplayStatus(StatusInfo newstatus); + void RedrawDisplay(); + DialogResult ShowEditVertices(ICollection vertices); + DialogResult ShowEditLinedefs(ICollection lines); + DialogResult ShowEditSectors(ICollection sectors); + DialogResult ShowEditThings(ICollection things); + void ShowLinedefInfo(Linedef l); + void ShowSectorInfo(Sector s); + void ShowThingInfo(Thing t); + void ShowVertexInfo(Vertex v); + void HideInfo(); + void RefreshInfo(); + void UpdateCoordinates(Vector2D coords); + bool Focus(); + void EnableProcessing(); + void DisableProcessing(); + void StartExclusiveMouseInput(); + void StopExclusiveMouseInput(); + void BreakExclusiveMouseInput(); + void ResumeExclusiveMouseInput(); + void SetCursor(Cursor cursor); + void MessageBeep(MessageBeepType type); + + /// + /// This moves the focus to the editing display. + /// + bool FocusDisplay(); + + /// + /// This browses the lindef types + /// + /// Returns the new action or the same action when cancelled + int BrowseLinedefActions(IWin32Window owner, int initialvalue); + + /// + /// This browses sector effects + /// + /// Returns the new effect or the same effect when cancelled + int BrowseSectorEffect(IWin32Window owner, int initialvalue); + + /// + /// This browses for a texture + /// + /// Returns the new texture name or the same texture name when cancelled + string BrowseTexture(IWin32Window owner, string initialvalue); + + /// + /// This browses for a flat + /// + /// Returns the new flat name or the same flat name when cancelled + string BrowseFlat(IWin32Window owner, string initialvalue); + + /// + /// THis browses for a thing type + /// + /// Returns the new thing type or the same thing type when cancelled + int BrowseThingType(IWin32Window owner, int initialvalue); + + /// + /// This adds a menu to the Doom Builder menu strip. + /// + /// NOTE: When the Tag property of menu items is set with a string, this changes the + /// tag to a fully qualified action name by prefixing it with the assembly name. + /// + /// + /// The menu to add to Doom Builder. + void AddMenu(ToolStripMenuItem menu); + + /// + /// This removes a menu from the Doom Builder menu strip. + /// + /// The menu to remove. + void RemoveMenu(ToolStripMenuItem menu); + + /// + /// This method invokes the action specified on the Tag property of the given menu item. + /// + /// Menu item with Tag property set to the name of an action + /// that you want to invoke. + /// Unused. + void InvokeTaggedAction(object sender, EventArgs e); + + /// + /// This adds a custom button to the toolbar. + /// + void AddButton(ToolStripItem button); + + /// + /// This removes a custom button from the toolbar. + /// + void RemoveButton(ToolStripItem button); + + /// + /// This adds a docker to the side panel. + /// + void AddDocker(Docker d); + + /// + /// This removes a docker from the side panel. + /// + bool RemoveDocker(Docker d); + + /// + /// Selects a docker in the side panel. + /// + bool SelectDocker(Docker d); + + /// + /// This selected the previously selected docker in the side panel. + /// + void SelectPreviousDocker(); + } +} diff --git a/Source/Core/Windows/LinedefEditForm.Designer.cs b/Source/Core/Windows/LinedefEditForm.Designer.cs new file mode 100644 index 0000000..fb05b18 --- /dev/null +++ b/Source/Core/Windows/LinedefEditForm.Designer.cs @@ -0,0 +1,839 @@ +namespace CodeImp.DoomBuilder.Windows +{ + partial class LinedefEditForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.Windows.Forms.Label labelAction; + System.Windows.Forms.Label taglabel; + System.Windows.Forms.Label switchtexturelbl; + System.Windows.Forms.Label displayswitchlbl; + System.Windows.Forms.Label labelFrontSecIndex; + System.Windows.Forms.Label labelFrontOffset; + System.Windows.Forms.Label labelFrontLower; + System.Windows.Forms.Label labelFrontMiddle; + System.Windows.Forms.Label labelFrontUpper; + System.Windows.Forms.Label labelBackSecIndex; + System.Windows.Forms.Label labelBackOffset; + System.Windows.Forms.Label labelBackLower; + System.Windows.Forms.Label labelBackMiddle; + System.Windows.Forms.Label labelBackUpper; + this.cancel = new System.Windows.Forms.Button(); + this.apply = new System.Windows.Forms.Button(); + this.actiongroup = new System.Windows.Forms.GroupBox(); + this.action = new CodeImp.DoomBuilder.Controls.ActionSelectorControl(); + this.browseaction = new System.Windows.Forms.Button(); + this.newtag = new System.Windows.Forms.Button(); + this.settingsgroup = new System.Windows.Forms.GroupBox(); + this.flags = new CodeImp.DoomBuilder.Controls.CheckboxArrayControl(); + this.linedefproperties = new System.Windows.Forms.Panel(); + this.backgroup = new System.Windows.Forms.GroupBox(); + this.backside = new System.Windows.Forms.CheckBox(); + this.backoffsety = new CodeImp.DoomBuilder.Controls.ButtonsNumericTextbox(); + this.backoffsetx = new CodeImp.DoomBuilder.Controls.ButtonsNumericTextbox(); + this.backsector = new CodeImp.DoomBuilder.Controls.ButtonsNumericTextbox(); + this.backlow = new CodeImp.DoomBuilder.Controls.TextureSelectorControl(); + this.backmid = new CodeImp.DoomBuilder.Controls.TextureSelectorControl(); + this.backhigh = new CodeImp.DoomBuilder.Controls.TextureSelectorControl(); + this.frontgroup = new System.Windows.Forms.GroupBox(); + this.frontside = new System.Windows.Forms.CheckBox(); + this.frontoffsety = new CodeImp.DoomBuilder.Controls.ButtonsNumericTextbox(); + this.frontoffsetx = new CodeImp.DoomBuilder.Controls.ButtonsNumericTextbox(); + this.frontsector = new CodeImp.DoomBuilder.Controls.ButtonsNumericTextbox(); + this.frontlow = new CodeImp.DoomBuilder.Controls.TextureSelectorControl(); + this.frontmid = new CodeImp.DoomBuilder.Controls.TextureSelectorControl(); + this.fronthigh = new CodeImp.DoomBuilder.Controls.TextureSelectorControl(); + this.switchsetupbox = new System.Windows.Forms.GroupBox(); + this.chkSwitchTextureLower = new System.Windows.Forms.CheckBox(); + this.chkSwitchDisplayLower = new System.Windows.Forms.CheckBox(); + this.chkSwitchTextureMiddle = new System.Windows.Forms.CheckBox(); + this.chkSwitchDisplayMiddle = new System.Windows.Forms.CheckBox(); + this.chkSwitchTextureUpper = new System.Windows.Forms.CheckBox(); + this.chkSwitchDisplayUpper = new System.Windows.Forms.CheckBox(); + this.activationtype = new System.Windows.Forms.GroupBox(); + this.activationtyperepeat = new System.Windows.Forms.CheckBox(); + this.activationtypeyellow = new System.Windows.Forms.CheckBox(); + this.activationtypered = new System.Windows.Forms.CheckBox(); + this.activationtypeblue = new System.Windows.Forms.CheckBox(); + this.activationtypeshoot = new System.Windows.Forms.CheckBox(); + this.activationtypecross = new System.Windows.Forms.CheckBox(); + this.activationtypeuse = new System.Windows.Forms.CheckBox(); + this.idgroup = new System.Windows.Forms.GroupBox(); + this.tag = new CodeImp.DoomBuilder.Controls.ButtonsNumericTextbox(); + this.heightpanel3 = new System.Windows.Forms.Panel(); + labelAction = new System.Windows.Forms.Label(); + taglabel = new System.Windows.Forms.Label(); + switchtexturelbl = new System.Windows.Forms.Label(); + displayswitchlbl = new System.Windows.Forms.Label(); + labelFrontSecIndex = new System.Windows.Forms.Label(); + labelFrontOffset = new System.Windows.Forms.Label(); + labelFrontLower = new System.Windows.Forms.Label(); + labelFrontMiddle = new System.Windows.Forms.Label(); + labelFrontUpper = new System.Windows.Forms.Label(); + labelBackSecIndex = new System.Windows.Forms.Label(); + labelBackOffset = new System.Windows.Forms.Label(); + labelBackLower = new System.Windows.Forms.Label(); + labelBackMiddle = new System.Windows.Forms.Label(); + labelBackUpper = new System.Windows.Forms.Label(); + this.actiongroup.SuspendLayout(); + this.settingsgroup.SuspendLayout(); + this.linedefproperties.SuspendLayout(); + this.backgroup.SuspendLayout(); + this.frontgroup.SuspendLayout(); + this.switchsetupbox.SuspendLayout(); + this.activationtype.SuspendLayout(); + this.idgroup.SuspendLayout(); + this.SuspendLayout(); + // + // labelAction + // + labelAction.AutoSize = true; + labelAction.Location = new System.Drawing.Point(15, 30); + labelAction.Name = "labelAction"; + labelAction.Size = new System.Drawing.Size(41, 14); + labelAction.TabIndex = 9; + labelAction.Text = "Action:"; + // + // taglabel + // + taglabel.AutoSize = true; + taglabel.Location = new System.Drawing.Point(28, 31); + taglabel.Name = "taglabel"; + taglabel.Size = new System.Drawing.Size(27, 14); + taglabel.TabIndex = 6; + taglabel.Text = "Tag:"; + // + // switchtexturelbl + // + switchtexturelbl.AutoSize = true; + switchtexturelbl.Location = new System.Drawing.Point(12, 56); + switchtexturelbl.Name = "switchtexturelbl"; + switchtexturelbl.Size = new System.Drawing.Size(83, 14); + switchtexturelbl.TabIndex = 20; + switchtexturelbl.Text = "Switch Texture:"; + // + // displayswitchlbl + // + displayswitchlbl.AutoSize = true; + displayswitchlbl.Location = new System.Drawing.Point(12, 34); + displayswitchlbl.Name = "displayswitchlbl"; + displayswitchlbl.Size = new System.Drawing.Size(82, 14); + displayswitchlbl.TabIndex = 19; + displayswitchlbl.Text = "Switch Display:"; + // + // labelFrontSecIndex + // + labelFrontSecIndex.AutoSize = true; + labelFrontSecIndex.Location = new System.Drawing.Point(16, 32); + labelFrontSecIndex.Name = "labelFrontSecIndex"; + labelFrontSecIndex.Size = new System.Drawing.Size(71, 14); + labelFrontSecIndex.TabIndex = 13; + labelFrontSecIndex.Text = "Sector Index:"; + // + // labelFrontOffset + // + labelFrontOffset.AutoSize = true; + labelFrontOffset.Location = new System.Drawing.Point(6, 71); + labelFrontOffset.Name = "labelFrontOffset"; + labelFrontOffset.Size = new System.Drawing.Size(80, 14); + labelFrontOffset.TabIndex = 7; + labelFrontOffset.Text = "Texture Offset:"; + // + // labelFrontLower + // + labelFrontLower.Location = new System.Drawing.Point(424, 10); + labelFrontLower.Name = "labelFrontLower"; + labelFrontLower.Size = new System.Drawing.Size(83, 16); + labelFrontLower.TabIndex = 5; + labelFrontLower.Text = "Lower"; + labelFrontLower.TextAlign = System.Drawing.ContentAlignment.TopCenter; + // + // labelFrontMiddle + // + labelFrontMiddle.Location = new System.Drawing.Point(333, 10); + labelFrontMiddle.Name = "labelFrontMiddle"; + labelFrontMiddle.Size = new System.Drawing.Size(83, 16); + labelFrontMiddle.TabIndex = 4; + labelFrontMiddle.Text = "Middle"; + labelFrontMiddle.TextAlign = System.Drawing.ContentAlignment.TopCenter; + // + // labelFrontUpper + // + labelFrontUpper.Location = new System.Drawing.Point(242, 10); + labelFrontUpper.Name = "labelFrontUpper"; + labelFrontUpper.Size = new System.Drawing.Size(83, 16); + labelFrontUpper.TabIndex = 3; + labelFrontUpper.Text = "Upper"; + labelFrontUpper.TextAlign = System.Drawing.ContentAlignment.TopCenter; + // + // labelBackSecIndex + // + labelBackSecIndex.AutoSize = true; + labelBackSecIndex.Location = new System.Drawing.Point(16, 32); + labelBackSecIndex.Name = "labelBackSecIndex"; + labelBackSecIndex.Size = new System.Drawing.Size(71, 14); + labelBackSecIndex.TabIndex = 16; + labelBackSecIndex.Text = "Sector Index:"; + // + // labelBackOffset + // + labelBackOffset.AutoSize = true; + labelBackOffset.Location = new System.Drawing.Point(6, 71); + labelBackOffset.Name = "labelBackOffset"; + labelBackOffset.Size = new System.Drawing.Size(80, 14); + labelBackOffset.TabIndex = 7; + labelBackOffset.Text = "Texture Offset:"; + // + // labelBackLower + // + labelBackLower.Location = new System.Drawing.Point(427, 10); + labelBackLower.Name = "labelBackLower"; + labelBackLower.Size = new System.Drawing.Size(83, 16); + labelBackLower.TabIndex = 5; + labelBackLower.Text = "Lower"; + labelBackLower.TextAlign = System.Drawing.ContentAlignment.TopCenter; + // + // labelBackMiddle + // + labelBackMiddle.Location = new System.Drawing.Point(336, 10); + labelBackMiddle.Name = "labelBackMiddle"; + labelBackMiddle.Size = new System.Drawing.Size(83, 16); + labelBackMiddle.TabIndex = 4; + labelBackMiddle.Text = "Middle"; + labelBackMiddle.TextAlign = System.Drawing.ContentAlignment.TopCenter; + // + // labelBackUpper + // + labelBackUpper.Location = new System.Drawing.Point(245, 10); + labelBackUpper.Name = "labelBackUpper"; + labelBackUpper.Size = new System.Drawing.Size(83, 16); + labelBackUpper.TabIndex = 3; + labelBackUpper.Text = "Upper"; + labelBackUpper.TextAlign = System.Drawing.ContentAlignment.TopCenter; + // + // cancel + // + this.cancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.cancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.cancel.Location = new System.Drawing.Point(937, 484); + this.cancel.Name = "cancel"; + this.cancel.Size = new System.Drawing.Size(112, 25); + this.cancel.TabIndex = 2; + this.cancel.Text = "Cancel"; + this.cancel.UseVisualStyleBackColor = true; + this.cancel.Click += new System.EventHandler(this.cancel_Click); + // + // apply + // + this.apply.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.apply.Location = new System.Drawing.Point(819, 484); + this.apply.Name = "apply"; + this.apply.Size = new System.Drawing.Size(112, 25); + this.apply.TabIndex = 1; + this.apply.Text = "OK"; + this.apply.UseVisualStyleBackColor = true; + this.apply.Click += new System.EventHandler(this.apply_Click); + // + // actiongroup + // + this.actiongroup.Controls.Add(labelAction); + this.actiongroup.Controls.Add(this.action); + this.actiongroup.Controls.Add(this.browseaction); + this.actiongroup.Location = new System.Drawing.Point(8, 218); + this.actiongroup.Name = "actiongroup"; + this.actiongroup.Size = new System.Drawing.Size(499, 68); + this.actiongroup.TabIndex = 1; + this.actiongroup.TabStop = false; + this.actiongroup.Text = " Action "; + // + // action + // + this.action.BackColor = System.Drawing.Color.Transparent; + this.action.Cursor = System.Windows.Forms.Cursors.Default; + this.action.Empty = false; + this.action.GeneralizedCategories = null; + this.action.Location = new System.Drawing.Point(62, 27); + this.action.Macro = false; + this.action.Name = "action"; + this.action.Size = new System.Drawing.Size(383, 21); + this.action.TabIndex = 0; + this.action.Value = 402; + this.action.ValueChanges += new System.EventHandler(this.action_ValueChanges); + // + // browseaction + // + this.browseaction.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.browseaction.Image = global::CodeImp.DoomBuilder.Properties.Resources.treeview; + this.browseaction.Location = new System.Drawing.Point(455, 26); + this.browseaction.Name = "browseaction"; + this.browseaction.Padding = new System.Windows.Forms.Padding(0, 0, 1, 3); + this.browseaction.Size = new System.Drawing.Size(30, 23); + this.browseaction.TabIndex = 1; + this.browseaction.Text = " "; + this.browseaction.UseVisualStyleBackColor = true; + this.browseaction.Click += new System.EventHandler(this.browseaction_Click); + // + // newtag + // + this.newtag.Location = new System.Drawing.Point(149, 27); + this.newtag.Name = "newtag"; + this.newtag.Size = new System.Drawing.Size(76, 23); + this.newtag.TabIndex = 1; + this.newtag.Text = "New Tag"; + this.newtag.UseVisualStyleBackColor = true; + this.newtag.Click += new System.EventHandler(this.newtag_Click); + // + // settingsgroup + // + this.settingsgroup.Controls.Add(this.flags); + this.settingsgroup.Location = new System.Drawing.Point(8, 8); + this.settingsgroup.Name = "settingsgroup"; + this.settingsgroup.Size = new System.Drawing.Size(499, 204); + this.settingsgroup.TabIndex = 0; + this.settingsgroup.TabStop = false; + this.settingsgroup.Text = " Settings "; + // + // flags + // + this.flags.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.flags.AutoScroll = true; + this.flags.Columns = 3; + this.flags.Location = new System.Drawing.Point(18, 26); + this.flags.Name = "flags"; + this.flags.Size = new System.Drawing.Size(475, 166); + this.flags.TabIndex = 0; + // + // linedefproperties + // + this.linedefproperties.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.linedefproperties.Controls.Add(this.backgroup); + this.linedefproperties.Controls.Add(this.frontgroup); + this.linedefproperties.Controls.Add(this.switchsetupbox); + this.linedefproperties.Controls.Add(this.activationtype); + this.linedefproperties.Controls.Add(this.idgroup); + this.linedefproperties.Controls.Add(this.settingsgroup); + this.linedefproperties.Controls.Add(this.actiongroup); + this.linedefproperties.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.linedefproperties.Location = new System.Drawing.Point(12, 0); + this.linedefproperties.Name = "linedefproperties"; + this.linedefproperties.Padding = new System.Windows.Forms.Padding(5); + this.linedefproperties.Size = new System.Drawing.Size(1037, 448); + this.linedefproperties.TabIndex = 0; + this.linedefproperties.Tag = "0"; + this.linedefproperties.Text = "Properties"; + // + // backgroup + // + this.backgroup.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.backgroup.Controls.Add(this.backside); + this.backgroup.Controls.Add(this.backoffsety); + this.backgroup.Controls.Add(this.backoffsetx); + this.backgroup.Controls.Add(this.backsector); + this.backgroup.Controls.Add(labelBackSecIndex); + this.backgroup.Controls.Add(this.backlow); + this.backgroup.Controls.Add(this.backmid); + this.backgroup.Controls.Add(this.backhigh); + this.backgroup.Controls.Add(labelBackOffset); + this.backgroup.Controls.Add(labelBackLower); + this.backgroup.Controls.Add(labelBackMiddle); + this.backgroup.Controls.Add(labelBackUpper); + this.backgroup.Enabled = false; + this.backgroup.Location = new System.Drawing.Point(513, 172); + this.backgroup.Name = "backgroup"; + this.backgroup.Size = new System.Drawing.Size(522, 158); + this.backgroup.TabIndex = 34; + this.backgroup.TabStop = false; + this.backgroup.Text = " "; + // + // backside + // + this.backside.AutoSize = true; + this.backside.Location = new System.Drawing.Point(11, -1); + this.backside.Name = "backside"; + this.backside.Size = new System.Drawing.Size(74, 18); + this.backside.TabIndex = 20; + this.backside.Text = "Back Side"; + this.backside.UseVisualStyleBackColor = true; + this.backside.CheckStateChanged += new System.EventHandler(this.backside_CheckStateChanged); + // + // backoffsety + // + this.backoffsety.AllowDecimal = false; + this.backoffsety.AllowNegative = true; + this.backoffsety.AllowRelative = true; + this.backoffsety.ButtonStep = 1; + this.backoffsety.Location = new System.Drawing.Point(161, 66); + this.backoffsety.Name = "backoffsety"; + this.backoffsety.Size = new System.Drawing.Size(62, 24); + this.backoffsety.StepValues = null; + this.backoffsety.TabIndex = 19; + // + // backoffsetx + // + this.backoffsetx.AllowDecimal = false; + this.backoffsetx.AllowNegative = true; + this.backoffsetx.AllowRelative = true; + this.backoffsetx.ButtonStep = 1; + this.backoffsetx.Location = new System.Drawing.Point(93, 66); + this.backoffsetx.Name = "backoffsetx"; + this.backoffsetx.Size = new System.Drawing.Size(62, 24); + this.backoffsetx.StepValues = null; + this.backoffsetx.TabIndex = 18; + // + // backsector + // + this.backsector.AllowDecimal = false; + this.backsector.AllowNegative = false; + this.backsector.AllowRelative = false; + this.backsector.ButtonStep = 1; + this.backsector.Location = new System.Drawing.Point(93, 27); + this.backsector.Name = "backsector"; + this.backsector.Size = new System.Drawing.Size(130, 24); + this.backsector.StepValues = null; + this.backsector.TabIndex = 17; + // + // backlow + // + this.backlow.Location = new System.Drawing.Point(427, 29); + this.backlow.Name = "backlow"; + this.backlow.Required = false; + this.backlow.Size = new System.Drawing.Size(83, 112); + this.backlow.TabIndex = 6; + this.backlow.TextureName = ""; + // + // backmid + // + this.backmid.Location = new System.Drawing.Point(336, 29); + this.backmid.Name = "backmid"; + this.backmid.Required = false; + this.backmid.Size = new System.Drawing.Size(83, 112); + this.backmid.TabIndex = 5; + this.backmid.TextureName = ""; + // + // backhigh + // + this.backhigh.Location = new System.Drawing.Point(245, 29); + this.backhigh.Name = "backhigh"; + this.backhigh.Required = false; + this.backhigh.Size = new System.Drawing.Size(83, 112); + this.backhigh.TabIndex = 4; + this.backhigh.TextureName = ""; + // + // frontgroup + // + this.frontgroup.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.frontgroup.Controls.Add(this.frontside); + this.frontgroup.Controls.Add(this.frontoffsety); + this.frontgroup.Controls.Add(this.frontoffsetx); + this.frontgroup.Controls.Add(this.frontsector); + this.frontgroup.Controls.Add(labelFrontSecIndex); + this.frontgroup.Controls.Add(this.frontlow); + this.frontgroup.Controls.Add(this.frontmid); + this.frontgroup.Controls.Add(this.fronthigh); + this.frontgroup.Controls.Add(labelFrontOffset); + this.frontgroup.Controls.Add(labelFrontLower); + this.frontgroup.Controls.Add(labelFrontMiddle); + this.frontgroup.Controls.Add(labelFrontUpper); + this.frontgroup.Enabled = false; + this.frontgroup.Location = new System.Drawing.Point(513, 8); + this.frontgroup.Name = "frontgroup"; + this.frontgroup.Size = new System.Drawing.Size(522, 158); + this.frontgroup.TabIndex = 33; + this.frontgroup.TabStop = false; + this.frontgroup.Text = " "; + // + // frontside + // + this.frontside.AutoSize = true; + this.frontside.Location = new System.Drawing.Point(6, 0); + this.frontside.Name = "frontside"; + this.frontside.Size = new System.Drawing.Size(75, 18); + this.frontside.TabIndex = 17; + this.frontside.Text = "Front Side"; + this.frontside.UseVisualStyleBackColor = true; + this.frontside.CheckStateChanged += new System.EventHandler(this.frontside_CheckStateChanged); + // + // frontoffsety + // + this.frontoffsety.AllowDecimal = false; + this.frontoffsety.AllowNegative = true; + this.frontoffsety.AllowRelative = true; + this.frontoffsety.ButtonStep = 1; + this.frontoffsety.Location = new System.Drawing.Point(161, 66); + this.frontoffsety.Name = "frontoffsety"; + this.frontoffsety.Size = new System.Drawing.Size(62, 24); + this.frontoffsety.StepValues = null; + this.frontoffsety.TabIndex = 16; + // + // frontoffsetx + // + this.frontoffsetx.AllowDecimal = false; + this.frontoffsetx.AllowNegative = true; + this.frontoffsetx.AllowRelative = true; + this.frontoffsetx.ButtonStep = 1; + this.frontoffsetx.Location = new System.Drawing.Point(93, 66); + this.frontoffsetx.Name = "frontoffsetx"; + this.frontoffsetx.Size = new System.Drawing.Size(62, 24); + this.frontoffsetx.StepValues = null; + this.frontoffsetx.TabIndex = 15; + // + // frontsector + // + this.frontsector.AllowDecimal = false; + this.frontsector.AllowNegative = false; + this.frontsector.AllowRelative = false; + this.frontsector.ButtonStep = 1; + this.frontsector.Location = new System.Drawing.Point(93, 27); + this.frontsector.Name = "frontsector"; + this.frontsector.Size = new System.Drawing.Size(130, 24); + this.frontsector.StepValues = null; + this.frontsector.TabIndex = 14; + // + // frontlow + // + this.frontlow.Location = new System.Drawing.Point(424, 29); + this.frontlow.Name = "frontlow"; + this.frontlow.Required = false; + this.frontlow.Size = new System.Drawing.Size(83, 112); + this.frontlow.TabIndex = 6; + this.frontlow.TextureName = ""; + // + // frontmid + // + this.frontmid.Location = new System.Drawing.Point(333, 29); + this.frontmid.Name = "frontmid"; + this.frontmid.Required = false; + this.frontmid.Size = new System.Drawing.Size(83, 112); + this.frontmid.TabIndex = 5; + this.frontmid.TextureName = ""; + // + // fronthigh + // + this.fronthigh.Location = new System.Drawing.Point(242, 29); + this.fronthigh.Name = "fronthigh"; + this.fronthigh.Required = false; + this.fronthigh.Size = new System.Drawing.Size(83, 112); + this.fronthigh.TabIndex = 4; + this.fronthigh.TextureName = ""; + // + // switchsetupbox + // + this.switchsetupbox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.switchsetupbox.Controls.Add(this.chkSwitchTextureLower); + this.switchsetupbox.Controls.Add(this.chkSwitchDisplayLower); + this.switchsetupbox.Controls.Add(this.chkSwitchTextureMiddle); + this.switchsetupbox.Controls.Add(this.chkSwitchDisplayMiddle); + this.switchsetupbox.Controls.Add(this.chkSwitchTextureUpper); + this.switchsetupbox.Controls.Add(this.chkSwitchDisplayUpper); + this.switchsetupbox.Controls.Add(switchtexturelbl); + this.switchsetupbox.Controls.Add(displayswitchlbl); + this.switchsetupbox.Location = new System.Drawing.Point(513, 336); + this.switchsetupbox.Name = "switchsetupbox"; + this.switchsetupbox.Size = new System.Drawing.Size(522, 100); + this.switchsetupbox.TabIndex = 32; + this.switchsetupbox.TabStop = false; + this.switchsetupbox.Text = "Switch Setup"; + // + // chkSwitchTextureLower + // + this.chkSwitchTextureLower.AutoSize = true; + this.chkSwitchTextureLower.Location = new System.Drawing.Point(240, 55); + this.chkSwitchTextureLower.Name = "chkSwitchTextureLower"; + this.chkSwitchTextureLower.Size = new System.Drawing.Size(58, 18); + this.chkSwitchTextureLower.TabIndex = 26; + this.chkSwitchTextureLower.Text = "Lower"; + this.chkSwitchTextureLower.UseVisualStyleBackColor = true; + this.chkSwitchTextureLower.CheckedChanged += new System.EventHandler(this.chkSwitchTextureLower_CheckedChanged_1); + // + // chkSwitchDisplayLower + // + this.chkSwitchDisplayLower.AutoSize = true; + this.chkSwitchDisplayLower.Location = new System.Drawing.Point(240, 33); + this.chkSwitchDisplayLower.Name = "chkSwitchDisplayLower"; + this.chkSwitchDisplayLower.Size = new System.Drawing.Size(58, 18); + this.chkSwitchDisplayLower.TabIndex = 25; + this.chkSwitchDisplayLower.Text = "Lower"; + this.chkSwitchDisplayLower.UseVisualStyleBackColor = true; + this.chkSwitchDisplayLower.CheckedChanged += new System.EventHandler(this.chkSwitchDisplayLower_CheckedChanged_1); + // + // chkSwitchTextureMiddle + // + this.chkSwitchTextureMiddle.AutoSize = true; + this.chkSwitchTextureMiddle.Location = new System.Drawing.Point(179, 55); + this.chkSwitchTextureMiddle.Name = "chkSwitchTextureMiddle"; + this.chkSwitchTextureMiddle.Size = new System.Drawing.Size(56, 18); + this.chkSwitchTextureMiddle.TabIndex = 24; + this.chkSwitchTextureMiddle.Text = "Middle"; + this.chkSwitchTextureMiddle.UseVisualStyleBackColor = true; + this.chkSwitchTextureMiddle.CheckedChanged += new System.EventHandler(this.chkSwitchTextureMiddle_CheckedChanged_1); + // + // chkSwitchDisplayMiddle + // + this.chkSwitchDisplayMiddle.AutoSize = true; + this.chkSwitchDisplayMiddle.Location = new System.Drawing.Point(178, 33); + this.chkSwitchDisplayMiddle.Name = "chkSwitchDisplayMiddle"; + this.chkSwitchDisplayMiddle.Size = new System.Drawing.Size(56, 18); + this.chkSwitchDisplayMiddle.TabIndex = 23; + this.chkSwitchDisplayMiddle.Text = "Middle"; + this.chkSwitchDisplayMiddle.UseVisualStyleBackColor = true; + this.chkSwitchDisplayMiddle.CheckedChanged += new System.EventHandler(this.chkSwitchDisplayMiddle_CheckedChanged_1); + // + // chkSwitchTextureUpper + // + this.chkSwitchTextureUpper.AutoSize = true; + this.chkSwitchTextureUpper.Location = new System.Drawing.Point(118, 55); + this.chkSwitchTextureUpper.Name = "chkSwitchTextureUpper"; + this.chkSwitchTextureUpper.Size = new System.Drawing.Size(55, 18); + this.chkSwitchTextureUpper.TabIndex = 22; + this.chkSwitchTextureUpper.Text = "Upper"; + this.chkSwitchTextureUpper.UseVisualStyleBackColor = true; + this.chkSwitchTextureUpper.CheckedChanged += new System.EventHandler(this.chkSwitchTextureUpper_CheckedChanged_1); + // + // chkSwitchDisplayUpper + // + this.chkSwitchDisplayUpper.AutoSize = true; + this.chkSwitchDisplayUpper.Location = new System.Drawing.Point(118, 33); + this.chkSwitchDisplayUpper.Name = "chkSwitchDisplayUpper"; + this.chkSwitchDisplayUpper.Size = new System.Drawing.Size(55, 18); + this.chkSwitchDisplayUpper.TabIndex = 21; + this.chkSwitchDisplayUpper.Text = "Upper"; + this.chkSwitchDisplayUpper.UseVisualStyleBackColor = true; + this.chkSwitchDisplayUpper.CheckedChanged += new System.EventHandler(this.chkSwitchDisplayUpper_CheckedChanged_1); + // + // activationtype + // + this.activationtype.Controls.Add(this.activationtyperepeat); + this.activationtype.Controls.Add(this.activationtypeyellow); + this.activationtype.Controls.Add(this.activationtypered); + this.activationtype.Controls.Add(this.activationtypeblue); + this.activationtype.Controls.Add(this.activationtypeshoot); + this.activationtype.Controls.Add(this.activationtypecross); + this.activationtype.Controls.Add(this.activationtypeuse); + this.activationtype.Location = new System.Drawing.Point(8, 364); + this.activationtype.Name = "activationtype"; + this.activationtype.Size = new System.Drawing.Size(499, 72); + this.activationtype.TabIndex = 31; + this.activationtype.TabStop = false; + this.activationtype.Text = "Activation Type"; + this.activationtype.Visible = false; + // + // activationtyperepeat + // + this.activationtyperepeat.AutoSize = true; + this.activationtyperepeat.Location = new System.Drawing.Point(284, 19); + this.activationtyperepeat.Name = "activationtyperepeat"; + this.activationtyperepeat.Size = new System.Drawing.Size(80, 18); + this.activationtyperepeat.TabIndex = 3; + this.activationtyperepeat.Text = "Repeatable"; + this.activationtyperepeat.UseVisualStyleBackColor = true; + // + // activationtypeyellow + // + this.activationtypeyellow.AutoSize = true; + this.activationtypeyellow.Location = new System.Drawing.Point(119, 43); + this.activationtypeyellow.Name = "activationtypeyellow"; + this.activationtypeyellow.Size = new System.Drawing.Size(81, 18); + this.activationtypeyellow.TabIndex = 6; + this.activationtypeyellow.Text = "Yellow Key"; + this.activationtypeyellow.UseVisualStyleBackColor = true; + // + // activationtypered + // + this.activationtypered.AutoSize = true; + this.activationtypered.Location = new System.Drawing.Point(207, 43); + this.activationtypered.Name = "activationtypered"; + this.activationtypered.Size = new System.Drawing.Size(67, 18); + this.activationtypered.TabIndex = 5; + this.activationtypered.Text = "Red Key"; + this.activationtypered.UseVisualStyleBackColor = true; + // + // activationtypeblue + // + this.activationtypeblue.AutoSize = true; + this.activationtypeblue.Location = new System.Drawing.Point(44, 43); + this.activationtypeblue.Name = "activationtypeblue"; + this.activationtypeblue.Size = new System.Drawing.Size(69, 18); + this.activationtypeblue.TabIndex = 4; + this.activationtypeblue.Text = "Blue Key"; + this.activationtypeblue.UseVisualStyleBackColor = true; + // + // activationtypeshoot + // + this.activationtypeshoot.AutoSize = true; + this.activationtypeshoot.Location = new System.Drawing.Point(207, 19); + this.activationtypeshoot.Name = "activationtypeshoot"; + this.activationtypeshoot.Size = new System.Drawing.Size(54, 18); + this.activationtypeshoot.TabIndex = 2; + this.activationtypeshoot.Text = "Shoot"; + this.activationtypeshoot.UseVisualStyleBackColor = true; + // + // activationtypecross + // + this.activationtypecross.AutoSize = true; + this.activationtypecross.Location = new System.Drawing.Point(119, 19); + this.activationtypecross.Name = "activationtypecross"; + this.activationtypecross.Size = new System.Drawing.Size(55, 18); + this.activationtypecross.TabIndex = 1; + this.activationtypecross.Text = "Cross"; + this.activationtypecross.UseVisualStyleBackColor = true; + // + // activationtypeuse + // + this.activationtypeuse.AutoSize = true; + this.activationtypeuse.Location = new System.Drawing.Point(44, 19); + this.activationtypeuse.Name = "activationtypeuse"; + this.activationtypeuse.Size = new System.Drawing.Size(45, 18); + this.activationtypeuse.TabIndex = 0; + this.activationtypeuse.Text = "Use"; + this.activationtypeuse.UseVisualStyleBackColor = true; + // + // idgroup + // + this.idgroup.Controls.Add(this.tag); + this.idgroup.Controls.Add(taglabel); + this.idgroup.Controls.Add(this.newtag); + this.idgroup.Location = new System.Drawing.Point(8, 292); + this.idgroup.Name = "idgroup"; + this.idgroup.Size = new System.Drawing.Size(499, 66); + this.idgroup.TabIndex = 2; + this.idgroup.TabStop = false; + this.idgroup.Text = " Identification "; + // + // tag + // + this.tag.AllowDecimal = false; + this.tag.AllowNegative = false; + this.tag.AllowRelative = true; + this.tag.ButtonStep = 1; + this.tag.Location = new System.Drawing.Point(62, 26); + this.tag.Name = "tag"; + this.tag.Size = new System.Drawing.Size(75, 24); + this.tag.StepValues = null; + this.tag.TabIndex = 7; + // + // heightpanel3 + // + this.heightpanel3.BackColor = System.Drawing.Color.Navy; + this.heightpanel3.Location = new System.Drawing.Point(128, -19); + this.heightpanel3.Name = "heightpanel3"; + this.heightpanel3.Size = new System.Drawing.Size(78, 519); + this.heightpanel3.TabIndex = 5; + this.heightpanel3.Visible = false; + // + // LinedefEditForm + // + this.AcceptButton = this.apply; + this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; + this.CancelButton = this.cancel; + this.ClientSize = new System.Drawing.Size(1059, 518); + this.Controls.Add(this.linedefproperties); + this.Controls.Add(this.cancel); + this.Controls.Add(this.apply); + this.Controls.Add(this.heightpanel3); + this.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "LinedefEditForm"; + this.Opacity = 0D; + this.ShowIcon = false; + this.ShowInTaskbar = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Edit Linedef"; + this.HelpRequested += new System.Windows.Forms.HelpEventHandler(this.LinedefEditForm_HelpRequested); + this.actiongroup.ResumeLayout(false); + this.actiongroup.PerformLayout(); + this.settingsgroup.ResumeLayout(false); + this.linedefproperties.ResumeLayout(false); + this.backgroup.ResumeLayout(false); + this.backgroup.PerformLayout(); + this.frontgroup.ResumeLayout(false); + this.frontgroup.PerformLayout(); + this.switchsetupbox.ResumeLayout(false); + this.switchsetupbox.PerformLayout(); + this.activationtype.ResumeLayout(false); + this.activationtype.PerformLayout(); + this.idgroup.ResumeLayout(false); + this.idgroup.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.Button cancel; + private System.Windows.Forms.Button apply; + private System.Windows.Forms.GroupBox actiongroup; + private System.Windows.Forms.GroupBox settingsgroup; + private CodeImp.DoomBuilder.Controls.CheckboxArrayControl flags; + private System.Windows.Forms.Button browseaction; + private CodeImp.DoomBuilder.Controls.ActionSelectorControl action; + private System.Windows.Forms.Button newtag; + private System.Windows.Forms.Panel linedefproperties; + private System.Windows.Forms.GroupBox idgroup; + private System.Windows.Forms.Panel heightpanel3; // villsa + private CodeImp.DoomBuilder.Controls.ButtonsNumericTextbox tag; + private System.Windows.Forms.GroupBox activationtype; + private System.Windows.Forms.CheckBox activationtyperepeat; + private System.Windows.Forms.CheckBox activationtypeyellow; + private System.Windows.Forms.CheckBox activationtypered; + private System.Windows.Forms.CheckBox activationtypeblue; + private System.Windows.Forms.CheckBox activationtypeshoot; + private System.Windows.Forms.CheckBox activationtypecross; + private System.Windows.Forms.CheckBox activationtypeuse; + private System.Windows.Forms.GroupBox switchsetupbox; + private System.Windows.Forms.CheckBox chkSwitchTextureLower; + private System.Windows.Forms.CheckBox chkSwitchDisplayLower; + private System.Windows.Forms.CheckBox chkSwitchTextureMiddle; + private System.Windows.Forms.CheckBox chkSwitchDisplayMiddle; + private System.Windows.Forms.CheckBox chkSwitchTextureUpper; + private System.Windows.Forms.CheckBox chkSwitchDisplayUpper; + private System.Windows.Forms.GroupBox frontgroup; + private System.Windows.Forms.CheckBox frontside; + private Controls.ButtonsNumericTextbox frontoffsety; + private Controls.ButtonsNumericTextbox frontoffsetx; + private Controls.ButtonsNumericTextbox frontsector; + private Controls.TextureSelectorControl frontlow; + private Controls.TextureSelectorControl frontmid; + private Controls.TextureSelectorControl fronthigh; + private System.Windows.Forms.GroupBox backgroup; + private System.Windows.Forms.CheckBox backside; + private Controls.ButtonsNumericTextbox backoffsety; + private Controls.ButtonsNumericTextbox backoffsetx; + private Controls.ButtonsNumericTextbox backsector; + private Controls.TextureSelectorControl backlow; + private Controls.TextureSelectorControl backmid; + private Controls.TextureSelectorControl backhigh; + } +} diff --git a/Source/Core/Windows/LinedefEditForm.cs b/Source/Core/Windows/LinedefEditForm.cs new file mode 100644 index 0000000..33d8c3d --- /dev/null +++ b/Source/Core/Windows/LinedefEditForm.cs @@ -0,0 +1,639 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Windows.Forms; +using CodeImp.DoomBuilder.Config; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Map; + + +#endregion + +namespace CodeImp.DoomBuilder.Windows +{ + internal partial class LinedefEditForm : DelayedForm + { + // Variables + private ICollection lines; + + // Constructor + public LinedefEditForm() + { + // Initialize + InitializeComponent(); + + // Fill flags list + foreach (KeyValuePair lf in General.Map.Config.LinedefFlags) + flags.Add(lf.Value, lf.Key); + + // Fill actions list + action.GeneralizedCategories = General.Map.Config.GenActionCategories; + action.AddInfo(General.Map.Config.SortedLinedefActions.ToArray()); + + // Initialize image selectors + fronthigh.Initialize(); + frontmid.Initialize(); + frontlow.Initialize(); + backhigh.Initialize(); + backmid.Initialize(); + backlow.Initialize(); + + // Tag? + if (General.Map.FormatInterface.HasLinedefTag) + { + // Match position after the action group + idgroup.Top = actiongroup.Bottom + actiongroup.Margin.Bottom + idgroup.Margin.Top; + } + else + { + idgroup.Visible = false; + } + + // villsa + if (General.Map.FormatInterface.InDoom64Mode) + { + this.activationtype.Show(); + activationtype.Top = idgroup.Bottom + idgroup.Margin.Bottom + activationtype.Margin.Top; + this.Height = heightpanel3.Height; + } + } + + // villsa 9/12/11 + private void SwitchTextureMask(Linedef l) + { + int switchflags = 0; + int mask = 0; + + if ((l.SwitchMask & 0x2000) == 0x2000) + switchflags |= 0x2000; + + if ((l.SwitchMask & 0x4000) == 0x4000) + switchflags |= 0x4000; + + if ((l.SwitchMask & 0x8000) == 0x8000) + switchflags |= 0x8000; + + if (l.IsFlagSet("65536")) + switchflags |= 65536; + + mask = (switchflags & 0x6000); + + if (mask == 0) + return; + + if (mask == 0x2000) + { + chkSwitchTextureUpper.Checked = true; + + if ((switchflags & 0x8000) == 0x8000) + chkSwitchDisplayMiddle.Checked = true; + else + chkSwitchDisplayLower.Checked = true; + } + else if (mask == 0x4000) + { + chkSwitchTextureLower.Checked = true; + + if ((switchflags & 0x10000) == 0x10000) + chkSwitchDisplayMiddle.Checked = true; + else if ((switchflags & 0x8000) == 0x8000) + chkSwitchDisplayUpper.Checked = true; + } + else if (mask == 0x6000) + { + chkSwitchTextureMiddle.Checked = true; + + if ((switchflags & 0x8000) == 0x8000) + chkSwitchDisplayUpper.Checked = true; + else + chkSwitchDisplayLower.Checked = true; + } + } + + // villsa 9/12/11 + private void SetSwitchMask(Linedef l) + { + int switchflags = 0; + + // just check for one of the checkboxes.. no need to + // check for all of them.. + if (chkSwitchTextureLower.Enabled == false) + return; + + if (chkSwitchTextureLower.Checked == true) + { + if (chkSwitchDisplayMiddle.Checked == true) + { + switchflags = 0x4000; + } + else if (chkSwitchDisplayUpper.Checked == true) + { + switchflags = (0x4000 | 0x8000); + } + } + else if (chkSwitchTextureMiddle.Checked == true) + { + if (chkSwitchDisplayLower.Checked == true) + { + switchflags = (0x2000 | 0x4000); + } + else if (chkSwitchDisplayUpper.Checked == true) + { + switchflags = (0x2000 | 0x4000 | 0x8000); + } + } + else if (chkSwitchTextureUpper.Checked == true) + { + if (chkSwitchDisplayMiddle.Checked == true) + { + switchflags = (0x2000 | 0x8000); + } + else if (chkSwitchDisplayLower.Checked == true) + { + switchflags = 0x2000; + } + } + + l.SwitchMask = switchflags; + } + + private void PreSetActivationFlag(CheckBox c, int flag, int mask) + { + if ((flag & mask) == mask) + c.Checked = true; + } + + private void CheckActivationState(CheckBox c, int flag, int mask) + { + if (((flag & mask) != mask) && c.Checked == true) + { + c.CheckState = CheckState.Indeterminate; + c.ThreeState = true; + } + } + + private void SetActivationFlag(Linedef l, CheckBox c, int mask) + { + if (c.CheckState == CheckState.Checked) + l.Activate |= mask; + else if (c.CheckState == CheckState.Unchecked) + l.Activate &= ~mask; + } + + // This sets up the form to edit the given lines + public void Setup(ICollection lines) + { + Linedef fl; + + // Keep this list + this.lines = lines; + if (lines.Count > 1) this.Text = "Edit Linedefs (" + lines.Count + ")"; + + //////////////////////////////////////////////////////////////////////// + // Set all options to the first linedef properties + //////////////////////////////////////////////////////////////////////// + + // Get first line + fl = General.GetByIndex(lines, 0); + + // Flags + foreach (CheckBox c in flags.Checkboxes) + if (fl.Flags.ContainsKey(c.Tag.ToString())) c.Checked = fl.Flags[c.Tag.ToString()]; + + // Action/tags + action.Value = fl.Action; + tag.Text = fl.Tag.ToString(); + + // Front side and back side checkboxes + frontside.Checked = (fl.Front != null); + backside.Checked = (fl.Back != null); + + // Front settings + if (fl.Front != null) + { + fronthigh.TextureName = fl.Front.HighTexture; + frontmid.TextureName = fl.Front.MiddleTexture; + frontlow.TextureName = fl.Front.LowTexture; + fronthigh.Required = fl.Front.HighRequired(); + frontmid.Required = fl.Front.MiddleRequired(); + frontlow.Required = fl.Front.LowRequired(); + frontsector.Text = fl.Front.Sector.Index.ToString(); + frontoffsetx.Text = fl.Front.OffsetX.ToString(); + frontoffsety.Text = fl.Front.OffsetY.ToString(); + } + + // Back settings + if (fl.Back != null) + { + backhigh.TextureName = fl.Back.HighTexture; + backmid.TextureName = fl.Back.MiddleTexture; + backlow.TextureName = fl.Back.LowTexture; + backhigh.Required = fl.Back.HighRequired(); + backmid.Required = fl.Back.MiddleRequired(); + backlow.Required = fl.Back.LowRequired(); + backsector.Text = fl.Back.Sector.Index.ToString(); + backoffsetx.Text = fl.Back.OffsetX.ToString(); + backoffsety.Text = fl.Back.OffsetY.ToString(); + } + + //////////////////////////////////////////////////////////////////////// + // Now go for all lines and change the options when a setting is different + //////////////////////////////////////////////////////////////////////// + + // Go for all lines + foreach (Linedef l in lines) + { + // Flags + foreach (CheckBox c in flags.Checkboxes) + { + if (l.Flags.ContainsKey(c.Tag.ToString())) + { + if (l.Flags[c.Tag.ToString()] != c.Checked) + { + c.ThreeState = true; + c.CheckState = CheckState.Indeterminate; + } + } + } + + // Activations + + // villsa + if (General.Map.FormatInterface.InDoom64Mode) + { + if (l.Activate > 0) + { + // 20120219 villsa + l.Activate -= (l.Activate & 511); + PreSetActivationFlag(activationtypered, l.Activate, 512); + PreSetActivationFlag(activationtypeblue, l.Activate, 1024); + PreSetActivationFlag(activationtypeyellow, l.Activate, 2048); + PreSetActivationFlag(activationtypecross, l.Activate, 4096); + PreSetActivationFlag(activationtypeshoot, l.Activate, 8192); + PreSetActivationFlag(activationtypeuse, l.Activate, 16384); + PreSetActivationFlag(activationtyperepeat, l.Activate, 32768); + } + + SwitchTextureMask(l); + } + + // Action/tags + if (l.Action != action.Value) action.Empty = true; + if (l.Tag.ToString() != tag.Text) tag.Text = ""; + + // Front side checkbox + if ((l.Front != null) != frontside.Checked) + { + frontside.ThreeState = true; + frontside.CheckState = CheckState.Indeterminate; + frontside.AutoCheck = false; + } + + // Back side checkbox + if ((l.Back != null) != backside.Checked) + { + backside.ThreeState = true; + backside.CheckState = CheckState.Indeterminate; + backside.AutoCheck = false; + } + + // Front settings + if (l.Front != null) + { + if (fronthigh.TextureName != l.Front.HighTexture) fronthigh.TextureName = ""; + if (frontmid.TextureName != l.Front.MiddleTexture) frontmid.TextureName = ""; + if (frontlow.TextureName != l.Front.LowTexture) frontlow.TextureName = ""; + if (fronthigh.Required != l.Front.HighRequired()) fronthigh.Required = false; + if (frontmid.Required != l.Front.MiddleRequired()) frontmid.Required = false; + if (frontlow.Required != l.Front.LowRequired()) frontlow.Required = false; + if (frontsector.Text != l.Front.Sector.Index.ToString()) frontsector.Text = ""; + if (frontoffsetx.Text != l.Front.OffsetX.ToString()) frontoffsetx.Text = ""; + if (frontoffsety.Text != l.Front.OffsetY.ToString()) frontoffsety.Text = ""; + } + + // Back settings + if (l.Back != null) + { + if (backhigh.TextureName != l.Back.HighTexture) backhigh.TextureName = ""; + if (backmid.TextureName != l.Back.MiddleTexture) backmid.TextureName = ""; + if (backlow.TextureName != l.Back.LowTexture) backlow.TextureName = ""; + if (backhigh.Required != l.Back.HighRequired()) backhigh.Required = false; + if (backmid.Required != l.Back.MiddleRequired()) backmid.Required = false; + if (backlow.Required != l.Back.LowRequired()) backlow.Required = false; + if (backsector.Text != l.Back.Sector.Index.ToString()) backsector.Text = ""; + if (backoffsetx.Text != l.Back.OffsetX.ToString()) backoffsetx.Text = ""; + if (backoffsety.Text != l.Back.OffsetY.ToString()) backoffsety.Text = ""; + } + } + + foreach (Linedef l in lines) + { + // 20120219 villsa + l.Activate -= (l.Activate & 511); + CheckActivationState(activationtypered, l.Activate, 512); + CheckActivationState(activationtypeblue, l.Activate, 1024); + CheckActivationState(activationtypeyellow, l.Activate, 2048); + CheckActivationState(activationtypecross, l.Activate, 4096); + CheckActivationState(activationtypeshoot, l.Activate, 8192); + CheckActivationState(activationtypeuse, l.Activate, 16384); + CheckActivationState(activationtyperepeat, l.Activate, 32768); + } + + // Refresh controls so that they show their image + backhigh.Refresh(); + backmid.Refresh(); + backlow.Refresh(); + fronthigh.Refresh(); + frontmid.Refresh(); + frontlow.Refresh(); + } + + // Front side (un)checked + private void frontside_CheckStateChanged(object sender, EventArgs e) + { + // Enable/disable panel + // NOTE: Also enabled when checkbox is grayed! + frontgroup.Enabled = (frontside.CheckState != CheckState.Unchecked); + } + + // Back side (un)checked + private void backside_CheckStateChanged(object sender, EventArgs e) + { + // Enable/disable panel + // NOTE: Also enabled when checkbox is grayed! + backgroup.Enabled = (backside.CheckState != CheckState.Unchecked); + } + + // This selects all text in a textbox + private void SelectAllText(object sender, EventArgs e) + { + (sender as TextBox).SelectAll(); + } + + // Apply clicked + private void apply_Click(object sender, EventArgs e) + { + string undodesc = "linedef"; + Sector s; + int index; + + // Verify the tag + if (General.Map.FormatInterface.HasLinedefTag && ((tag.GetResult(0) < General.Map.FormatInterface.MinTag) || (tag.GetResult(0) > General.Map.FormatInterface.MaxTag))) + { + General.ShowWarningMessage("Linedef tag must be between " + General.Map.FormatInterface.MinTag + " and " + General.Map.FormatInterface.MaxTag + ".", MessageBoxButtons.OK); + return; + } + + // Verify the action + if ((action.Value < General.Map.FormatInterface.MinAction) || (action.Value > General.Map.FormatInterface.MaxAction)) + { + General.ShowWarningMessage("Linedef action must be between " + General.Map.FormatInterface.MinAction + " and " + General.Map.FormatInterface.MaxAction + ".", MessageBoxButtons.OK); + return; + } + + // Verify texture offsets + if ((backoffsetx.GetResult(0) < General.Map.FormatInterface.MinTextureOffset) || (backoffsetx.GetResult(0) > General.Map.FormatInterface.MaxTextureOffset) || + (backoffsety.GetResult(0) < General.Map.FormatInterface.MinTextureOffset) || (backoffsety.GetResult(0) > General.Map.FormatInterface.MaxTextureOffset) || + (frontoffsetx.GetResult(0) < General.Map.FormatInterface.MinTextureOffset) || (frontoffsetx.GetResult(0) > General.Map.FormatInterface.MaxTextureOffset) || + (frontoffsety.GetResult(0) < General.Map.FormatInterface.MinTextureOffset) || (frontoffsety.GetResult(0) > General.Map.FormatInterface.MaxTextureOffset)) + { + General.ShowWarningMessage("Texture offset must be between " + General.Map.FormatInterface.MinTextureOffset + " and " + General.Map.FormatInterface.MaxTextureOffset + ".", MessageBoxButtons.OK); + return; + } + + // Make undo + if (lines.Count > 1) undodesc = lines.Count + " linedefs"; + General.Map.UndoRedo.CreateUndo("Edit " + undodesc); + + // Go for all the lines + foreach (Linedef l in lines) + { + // Apply all flags + foreach (CheckBox c in flags.Checkboxes) + { + if (c.CheckState == CheckState.Checked) l.SetFlag(c.Tag.ToString(), true); + else if (c.CheckState == CheckState.Unchecked) l.SetFlag(c.Tag.ToString(), false); + } + + l.Activate -= (l.Activate & 511); + SetActivationFlag(l, activationtypered, 512); + SetActivationFlag(l, activationtypeblue, 1024); + SetActivationFlag(l, activationtypeyellow, 2048); + SetActivationFlag(l, activationtypecross, 4096); + SetActivationFlag(l, activationtypeshoot, 8192); + SetActivationFlag(l, activationtypeuse, 16384); + SetActivationFlag(l, activationtyperepeat, 32768); + + SetSwitchMask(l); + + // Remove front side? + if ((l.Front != null) && (frontside.CheckState == CheckState.Unchecked)) + { + l.Front.Dispose(); + } + // Create or modify front side? + else if (frontside.CheckState == CheckState.Checked) + { + // Make sure we have a valid sector (make a new one if needed) + if (l.Front != null) index = l.Front.Sector.Index; else index = -1; + index = frontsector.GetResult(index); + if ((index > -1) && (index < General.Map.Map.Sectors.Count)) + { + s = General.Map.Map.GetSectorByIndex(index); + if (s == null) s = General.Map.Map.CreateSector(); + if (s != null) + { + // Create new sidedef? + if (l.Front == null) General.Map.Map.CreateSidedef(l, true, s); + if (l.Front != null) + { + // Change sector? + if (l.Front.Sector != s) l.Front.SetSector(s); + + // Apply settings + l.Front.OffsetX = General.Clamp(frontoffsetx.GetResult(l.Front.OffsetX), General.Map.FormatInterface.MinTextureOffset, General.Map.FormatInterface.MaxTextureOffset); + l.Front.OffsetY = General.Clamp(frontoffsety.GetResult(l.Front.OffsetY), General.Map.FormatInterface.MinTextureOffset, General.Map.FormatInterface.MaxTextureOffset); + l.Front.SetTextureHigh(fronthigh.GetResult(l.Front.HighTexture)); + l.Front.SetTextureMid(frontmid.GetResult(l.Front.MiddleTexture)); + l.Front.SetTextureLow(frontlow.GetResult(l.Front.LowTexture)); + } + } + } + } + + // Remove back side? + if ((l.Back != null) && (backside.CheckState == CheckState.Unchecked)) + { + l.Back.Dispose(); + } + // Create or modify back side? + else if (backside.CheckState == CheckState.Checked) + { + // Make sure we have a valid sector (make a new one if needed) + if (l.Back != null) index = l.Back.Sector.Index; else index = -1; + index = backsector.GetResult(index); + if ((index > -1) && (index < General.Map.Map.Sectors.Count)) + { + s = General.Map.Map.GetSectorByIndex(index); + if (s == null) s = General.Map.Map.CreateSector(); + if (s != null) + { + // Create new sidedef? + if (l.Back == null) General.Map.Map.CreateSidedef(l, false, s); + if (l.Back != null) + { + // Change sector? + if (l.Back.Sector != s) l.Back.SetSector(s); + + // Apply settings + l.Back.OffsetX = General.Clamp(backoffsetx.GetResult(l.Back.OffsetX), General.Map.FormatInterface.MinTextureOffset, General.Map.FormatInterface.MaxTextureOffset); + l.Back.OffsetY = General.Clamp(backoffsety.GetResult(l.Back.OffsetY), General.Map.FormatInterface.MinTextureOffset, General.Map.FormatInterface.MaxTextureOffset); + l.Back.SetTextureHigh(backhigh.GetResult(l.Back.HighTexture)); + l.Back.SetTextureMid(backmid.GetResult(l.Back.MiddleTexture)); + l.Back.SetTextureLow(backlow.GetResult(l.Back.LowTexture)); + } + } + } + } + } + + // Update the used textures + General.Map.Data.UpdateUsedTextures(); + + // Done + General.Map.IsChanged = true; + this.DialogResult = DialogResult.OK; + this.Close(); + } + + // Cancel clicked + private void cancel_Click(object sender, EventArgs e) + { + // Be gone + this.DialogResult = DialogResult.Cancel; + this.Close(); + } + + // This finds a new (unused) tag + private void newtag_Click(object sender, EventArgs e) + { + tag.Text = General.Map.Map.GetNewTag().ToString(); + } + + // Action changes + private void action_ValueChanges(object sender, EventArgs e) + { + int showaction = 0; + + // Only when line type is known + if (General.Map.Config.LinedefActions.ContainsKey(action.Value)) showaction = action.Value; + } + + // Browse Action clicked + private void browseaction_Click(object sender, EventArgs e) + { + action.Value = ActionBrowserForm.BrowseAction(this, action.Value); + } + + // Custom fields on front sides + private void customfrontbutton_Click(object sender, EventArgs e) + { + // Make collection of front sides + List sides = new List(lines.Count); + foreach (Linedef l in lines) if (l.Front != null) sides.Add(l.Front); + + // Edit these + CustomFieldsForm.ShowDialog(this, "Front side custom fields", "sidedef", sides, General.Map.Config.SidedefFields); + } + + // Custom fields on back sides + private void custombackbutton_Click(object sender, EventArgs e) + { + // Make collection of back sides + List sides = new List(lines.Count); + foreach (Linedef l in lines) if (l.Back != null) sides.Add(l.Back); + + // Edit these + CustomFieldsForm.ShowDialog(this, "Back side custom fields", "sidedef", sides, General.Map.Config.SidedefFields); + } + + // Help! + private void LinedefEditForm_HelpRequested(object sender, HelpEventArgs hlpevent) + { + General.ShowHelp("w_linedefedit.html"); + hlpevent.Handled = true; + } + + private void chkSwitchDisplayUpper_CheckedChanged_1(object sender, EventArgs e) + { + if (this.chkSwitchDisplayUpper.Checked) + { + this.chkSwitchDisplayLower.Checked = false; + this.chkSwitchDisplayMiddle.Checked = false; + } + } + + private void chkSwitchDisplayMiddle_CheckedChanged_1(object sender, EventArgs e) + { + if (this.chkSwitchDisplayMiddle.Checked) + { + this.chkSwitchDisplayLower.Checked = false; + this.chkSwitchDisplayUpper.Checked = false; + } + } + + private void chkSwitchDisplayLower_CheckedChanged_1(object sender, EventArgs e) + { + if (this.chkSwitchDisplayLower.Checked) + { + this.chkSwitchDisplayUpper.Checked = false; + this.chkSwitchDisplayMiddle.Checked = false; + } + } + + private void chkSwitchTextureUpper_CheckedChanged_1(object sender, EventArgs e) + { + if (this.chkSwitchTextureUpper.Checked) + { + this.chkSwitchTextureLower.Checked = false; + this.chkSwitchTextureMiddle.Checked = false; + } + } + + private void chkSwitchTextureMiddle_CheckedChanged_1(object sender, EventArgs e) + { + if (this.chkSwitchTextureMiddle.Checked) + { + this.chkSwitchTextureLower.Checked = false; + this.chkSwitchTextureUpper.Checked = false; + } + } + + private void chkSwitchTextureLower_CheckedChanged_1(object sender, EventArgs e) + { + if (this.chkSwitchTextureLower.Checked) + { + this.chkSwitchTextureUpper.Checked = false; + this.chkSwitchTextureMiddle.Checked = false; + } + } + } +} diff --git a/Source/Core/Windows/LinedefEditForm.resx b/Source/Core/Windows/LinedefEditForm.resx new file mode 100644 index 0000000..6af6be7 --- /dev/null +++ b/Source/Core/Windows/LinedefEditForm.resx @@ -0,0 +1,162 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + \ No newline at end of file diff --git a/Source/Core/Windows/MainForm.Designer.cs b/Source/Core/Windows/MainForm.Designer.cs new file mode 100644 index 0000000..e8b73d2 --- /dev/null +++ b/Source/Core/Windows/MainForm.Designer.cs @@ -0,0 +1,1874 @@ +namespace CodeImp.DoomBuilder.Windows +{ + partial class MainForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + System.Windows.Forms.ToolStripSeparator toolStripMenuItem1; + System.Windows.Forms.ToolStripSeparator toolStripMenuItem3; + System.Windows.Forms.ToolStripSeparator toolStripSeparator1; + System.Windows.Forms.ToolStripSeparator toolStripSeparator9; + System.Windows.Forms.ToolStripSeparator toolStripSeparator3; + System.Windows.Forms.ToolStripSeparator toolStripSeparator10; + System.Windows.Forms.ToolStripSeparator toolStripSeparator11; + System.Windows.Forms.ToolStripSeparator toolstripSeperator1; + System.Windows.Forms.ToolStripSeparator toolstripSeperator6; + System.Windows.Forms.ToolStripSeparator toolStripSeparator7; + System.Windows.Forms.ToolStripSeparator toolStripSeparator12; + System.Windows.Forms.ToolStripSeparator toolStripSeparator4; + System.Windows.Forms.ToolStripSeparator toolStripMenuItem4; + System.Windows.Forms.ToolStripSeparator toolStripSeparator2; + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MainForm)); + this.display = new CodeImp.DoomBuilder.Controls.RenderTargetControl(); + this.buttoneditmodesseperator = new System.Windows.Forms.ToolStripSeparator(); + this.poscommalabel = new System.Windows.Forms.ToolStripStatusLabel(); + this.menumain = new System.Windows.Forms.MenuStrip(); + this.menufile = new System.Windows.Forms.ToolStripMenuItem(); + this.itemnewmap = new System.Windows.Forms.ToolStripMenuItem(); + this.itemopenmap = new System.Windows.Forms.ToolStripMenuItem(); + this.itemclosemap = new System.Windows.Forms.ToolStripMenuItem(); + this.itemsavemap = new System.Windows.Forms.ToolStripMenuItem(); + this.itemsavemapas = new System.Windows.Forms.ToolStripMenuItem(); + this.itemsavemapinto = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem5 = new System.Windows.Forms.ToolStripSeparator(); + this.itemnorecent = new System.Windows.Forms.ToolStripMenuItem(); + this.itemexit = new System.Windows.Forms.ToolStripMenuItem(); + this.menuedit = new System.Windows.Forms.ToolStripMenuItem(); + this.itemundo = new System.Windows.Forms.ToolStripMenuItem(); + this.itemredo = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem7 = new System.Windows.Forms.ToolStripSeparator(); + this.itemcut = new System.Windows.Forms.ToolStripMenuItem(); + this.itemcopy = new System.Windows.Forms.ToolStripMenuItem(); + this.itempaste = new System.Windows.Forms.ToolStripMenuItem(); + this.itempastespecial = new System.Windows.Forms.ToolStripMenuItem(); + this.itemsnaptogrid = new System.Windows.Forms.ToolStripMenuItem(); + this.itemautomerge = new System.Windows.Forms.ToolStripMenuItem(); + this.itemfullbrightness = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem6 = new System.Windows.Forms.ToolStripSeparator(); + this.itemgridinc = new System.Windows.Forms.ToolStripMenuItem(); + this.itemgriddec = new System.Windows.Forms.ToolStripMenuItem(); + this.itemgridsetup = new System.Windows.Forms.ToolStripMenuItem(); + this.itemmapoptions = new System.Windows.Forms.ToolStripMenuItem(); + this.menuview = new System.Windows.Forms.ToolStripMenuItem(); + this.itemthingsfilter = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem9 = new System.Windows.Forms.ToolStripSeparator(); + this.itemviewnormal = new System.Windows.Forms.ToolStripMenuItem(); + this.itemviewfloors = new System.Windows.Forms.ToolStripMenuItem(); + this.itemviewceilings = new System.Windows.Forms.ToolStripMenuItem(); + this.itemviewfloorcolor = new System.Windows.Forms.ToolStripMenuItem(); + this.itemviewceilingcolor = new System.Windows.Forms.ToolStripMenuItem(); + this.itemviewthingcolor = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem2 = new System.Windows.Forms.ToolStripSeparator(); + this.menuzoom = new System.Windows.Forms.ToolStripMenuItem(); + this.item2zoom200 = new System.Windows.Forms.ToolStripMenuItem(); + this.item2zoom100 = new System.Windows.Forms.ToolStripMenuItem(); + this.item2zoom50 = new System.Windows.Forms.ToolStripMenuItem(); + this.item2zoom25 = new System.Windows.Forms.ToolStripMenuItem(); + this.item2zoom10 = new System.Windows.Forms.ToolStripMenuItem(); + this.item2zoom5 = new System.Windows.Forms.ToolStripMenuItem(); + this.itemfittoscreen = new System.Windows.Forms.ToolStripMenuItem(); + this.itemtoggleinfo = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem10 = new System.Windows.Forms.ToolStripSeparator(); + this.itemscripteditor = new System.Windows.Forms.ToolStripMenuItem(); + this.menumode = new System.Windows.Forms.ToolStripMenuItem(); + this.menuprefabs = new System.Windows.Forms.ToolStripMenuItem(); + this.iteminsertprefabfile = new System.Windows.Forms.ToolStripMenuItem(); + this.iteminsertpreviousprefab = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem12 = new System.Windows.Forms.ToolStripSeparator(); + this.itemcreateprefab = new System.Windows.Forms.ToolStripMenuItem(); + this.menutools = new System.Windows.Forms.ToolStripMenuItem(); + this.itemreloadresources = new System.Windows.Forms.ToolStripMenuItem(); + this.itemshowerrors = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem8 = new System.Windows.Forms.ToolStripSeparator(); + this.configurationToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.preferencesToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem11 = new System.Windows.Forms.ToolStripSeparator(); + this.itemtestmap = new System.Windows.Forms.ToolStripMenuItem(); + this.menuhelp = new System.Windows.Forms.ToolStripMenuItem(); + this.itemhelprefmanual = new System.Windows.Forms.ToolStripMenuItem(); + this.itemhelpeditmode = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem13 = new System.Windows.Forms.ToolStripSeparator(); + this.itemhelpabout = new System.Windows.Forms.ToolStripMenuItem(); + this.toolbar = new System.Windows.Forms.ToolStrip(); + this.buttonnewmap = new System.Windows.Forms.ToolStripButton(); + this.buttonopenmap = new System.Windows.Forms.ToolStripButton(); + this.buttonsavemap = new System.Windows.Forms.ToolStripButton(); + this.buttonmapoptions = new System.Windows.Forms.ToolStripButton(); + this.buttonscripteditor = new System.Windows.Forms.ToolStripButton(); + this.buttonundo = new System.Windows.Forms.ToolStripButton(); + this.buttonredo = new System.Windows.Forms.ToolStripButton(); + this.buttoncut = new System.Windows.Forms.ToolStripButton(); + this.buttoncopy = new System.Windows.Forms.ToolStripButton(); + this.buttonpaste = new System.Windows.Forms.ToolStripButton(); + this.buttoninsertprefabfile = new System.Windows.Forms.ToolStripButton(); + this.buttoninsertpreviousprefab = new System.Windows.Forms.ToolStripButton(); + this.buttonthingsfilter = new System.Windows.Forms.ToolStripButton(); + this.thingfilters = new System.Windows.Forms.ToolStripComboBox(); + this.buttonviewnormal = new System.Windows.Forms.ToolStripButton(); + this.buttonviewfloors = new System.Windows.Forms.ToolStripButton(); + this.buttonviewceilings = new System.Windows.Forms.ToolStripButton(); + this.buttonviewfloorcolor = new System.Windows.Forms.ToolStripButton(); + this.buttonviewceilingcolor = new System.Windows.Forms.ToolStripButton(); + this.buttonviewthingcolor = new System.Windows.Forms.ToolStripButton(); + this.toolStripSeparator8 = new System.Windows.Forms.ToolStripSeparator(); + this.buttonsnaptogrid = new System.Windows.Forms.ToolStripButton(); + this.buttonautomerge = new System.Windows.Forms.ToolStripButton(); + this.buttonfullbrightness = new System.Windows.Forms.ToolStripButton(); + this.toolStripSeparator5 = new System.Windows.Forms.ToolStripSeparator(); + this.buttontest = new System.Windows.Forms.ToolStripSplitButton(); + this.toolStripSeparator6 = new System.Windows.Forms.ToolStripSeparator(); + this.statusbar = new System.Windows.Forms.StatusStrip(); + this.statuslabel = new System.Windows.Forms.ToolStripStatusLabel(); + this.configlabel = new System.Windows.Forms.ToolStripStatusLabel(); + this.gridlabel = new System.Windows.Forms.ToolStripStatusLabel(); + this.buttongrid = new System.Windows.Forms.ToolStripDropDownButton(); + this.itemgrid1024 = new System.Windows.Forms.ToolStripMenuItem(); + this.itemgrid512 = new System.Windows.Forms.ToolStripMenuItem(); + this.itemgrid256 = new System.Windows.Forms.ToolStripMenuItem(); + this.itemgrid128 = new System.Windows.Forms.ToolStripMenuItem(); + this.itemgrid64 = new System.Windows.Forms.ToolStripMenuItem(); + this.itemgrid32 = new System.Windows.Forms.ToolStripMenuItem(); + this.itemgrid16 = new System.Windows.Forms.ToolStripMenuItem(); + this.itemgrid8 = new System.Windows.Forms.ToolStripMenuItem(); + this.itemgrid4 = new System.Windows.Forms.ToolStripMenuItem(); + this.itemgridcustom = new System.Windows.Forms.ToolStripMenuItem(); + this.zoomlabel = new System.Windows.Forms.ToolStripStatusLabel(); + this.buttonzoom = new System.Windows.Forms.ToolStripDropDownButton(); + this.itemzoom200 = new System.Windows.Forms.ToolStripMenuItem(); + this.itemzoom100 = new System.Windows.Forms.ToolStripMenuItem(); + this.itemzoom50 = new System.Windows.Forms.ToolStripMenuItem(); + this.itemzoom25 = new System.Windows.Forms.ToolStripMenuItem(); + this.itemzoom10 = new System.Windows.Forms.ToolStripMenuItem(); + this.itemzoom5 = new System.Windows.Forms.ToolStripMenuItem(); + this.itemzoomfittoscreen = new System.Windows.Forms.ToolStripMenuItem(); + this.xposlabel = new System.Windows.Forms.ToolStripStatusLabel(); + this.yposlabel = new System.Windows.Forms.ToolStripStatusLabel(); + this.panelinfo = new System.Windows.Forms.Panel(); + this.heightpanel1 = new System.Windows.Forms.Panel(); + this.vertexinfo = new CodeImp.DoomBuilder.Controls.VertexInfoPanel(); + this.labelcollapsedinfo = new System.Windows.Forms.Label(); + this.buttontoggleinfo = new System.Windows.Forms.Button(); + this.modename = new System.Windows.Forms.Label(); + this.linedefinfo = new CodeImp.DoomBuilder.Controls.LinedefInfoPanel(); + this.thinginfo = new CodeImp.DoomBuilder.Controls.ThingInfoPanel(); + this.sectorinfo = new CodeImp.DoomBuilder.Controls.SectorInfoPanel(); + this.redrawtimer = new System.Windows.Forms.Timer(this.components); + this.processor = new System.Windows.Forms.Timer(this.components); + this.statusflasher = new System.Windows.Forms.Timer(this.components); + this.statusresetter = new System.Windows.Forms.Timer(this.components); + this.dockersspace = new System.Windows.Forms.Panel(); + this.dockerspanel = new CodeImp.DoomBuilder.Controls.DockersControl(); + this.dockerscollapser = new System.Windows.Forms.Timer(this.components); + toolStripMenuItem1 = new System.Windows.Forms.ToolStripSeparator(); + toolStripMenuItem3 = new System.Windows.Forms.ToolStripSeparator(); + toolStripSeparator1 = new System.Windows.Forms.ToolStripSeparator(); + toolStripSeparator9 = new System.Windows.Forms.ToolStripSeparator(); + toolStripSeparator3 = new System.Windows.Forms.ToolStripSeparator(); + toolStripSeparator10 = new System.Windows.Forms.ToolStripSeparator(); + toolStripSeparator11 = new System.Windows.Forms.ToolStripSeparator(); + toolstripSeperator1 = new System.Windows.Forms.ToolStripSeparator(); + toolstripSeperator6 = new System.Windows.Forms.ToolStripSeparator(); + toolStripSeparator7 = new System.Windows.Forms.ToolStripSeparator(); + toolStripSeparator12 = new System.Windows.Forms.ToolStripSeparator(); + toolStripSeparator4 = new System.Windows.Forms.ToolStripSeparator(); + toolStripMenuItem4 = new System.Windows.Forms.ToolStripSeparator(); + toolStripSeparator2 = new System.Windows.Forms.ToolStripSeparator(); + this.menumain.SuspendLayout(); + this.toolbar.SuspendLayout(); + this.statusbar.SuspendLayout(); + this.panelinfo.SuspendLayout(); + this.SuspendLayout(); + // + // toolStripMenuItem1 + // + toolStripMenuItem1.Margin = new System.Windows.Forms.Padding(0, 3, 0, 3); + toolStripMenuItem1.Name = "toolStripMenuItem1"; + toolStripMenuItem1.Size = new System.Drawing.Size(198, 6); + // + // toolStripMenuItem3 + // + toolStripMenuItem3.Margin = new System.Windows.Forms.Padding(0, 3, 0, 3); + toolStripMenuItem3.Name = "toolStripMenuItem3"; + toolStripMenuItem3.Size = new System.Drawing.Size(198, 6); + // + // toolStripSeparator1 + // + toolStripSeparator1.Margin = new System.Windows.Forms.Padding(3, 0, 3, 0); + toolStripSeparator1.Name = "toolStripSeparator1"; + toolStripSeparator1.Size = new System.Drawing.Size(6, 23); + // + // toolStripSeparator9 + // + toolStripSeparator9.Margin = new System.Windows.Forms.Padding(3, 0, 3, 0); + toolStripSeparator9.Name = "toolStripSeparator9"; + toolStripSeparator9.Size = new System.Drawing.Size(6, 23); + // + // toolStripSeparator3 + // + toolStripSeparator3.Margin = new System.Windows.Forms.Padding(6, 0, 6, 0); + toolStripSeparator3.Name = "toolStripSeparator3"; + toolStripSeparator3.Size = new System.Drawing.Size(6, 25); + // + // toolStripSeparator10 + // + toolStripSeparator10.Margin = new System.Windows.Forms.Padding(6, 0, 6, 0); + toolStripSeparator10.Name = "toolStripSeparator10"; + toolStripSeparator10.Size = new System.Drawing.Size(6, 25); + // + // toolStripSeparator11 + // + toolStripSeparator11.Margin = new System.Windows.Forms.Padding(0, 3, 0, 3); + toolStripSeparator11.Name = "toolStripSeparator11"; + toolStripSeparator11.Size = new System.Drawing.Size(162, 6); + // + // toolstripSeperator1 + // + toolstripSeperator1.Margin = new System.Windows.Forms.Padding(6, 0, 6, 0); + toolstripSeperator1.Name = "toolstripSeperator1"; + toolstripSeperator1.Size = new System.Drawing.Size(6, 25); + // + // toolstripSeperator6 + // + toolstripSeperator6.Margin = new System.Windows.Forms.Padding(0, 3, 0, 3); + toolstripSeperator6.Name = "toolstripSeperator6"; + toolstripSeperator6.Size = new System.Drawing.Size(162, 6); + // + // toolStripSeparator7 + // + toolStripSeparator7.Margin = new System.Windows.Forms.Padding(6, 0, 6, 0); + toolStripSeparator7.Name = "toolStripSeparator7"; + toolStripSeparator7.Size = new System.Drawing.Size(6, 25); + // + // toolStripSeparator12 + // + toolStripSeparator12.Margin = new System.Windows.Forms.Padding(3, 0, 3, 0); + toolStripSeparator12.Name = "toolStripSeparator12"; + toolStripSeparator12.Size = new System.Drawing.Size(6, 23); + // + // toolStripSeparator4 + // + toolStripSeparator4.Margin = new System.Windows.Forms.Padding(6, 0, 6, 0); + toolStripSeparator4.Name = "toolStripSeparator4"; + toolStripSeparator4.Size = new System.Drawing.Size(6, 25); + // + // toolStripMenuItem4 + // + toolStripMenuItem4.Name = "toolStripMenuItem4"; + toolStripMenuItem4.Size = new System.Drawing.Size(161, 6); + // + // toolStripSeparator2 + // + toolStripSeparator2.Name = "toolStripSeparator2"; + toolStripSeparator2.Size = new System.Drawing.Size(164, 6); + // + // display + // + this.display.BackColor = System.Drawing.SystemColors.AppWorkspace; + this.display.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Center; + this.display.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D; + this.display.CausesValidation = false; + this.display.Location = new System.Drawing.Point(373, 141); + this.display.Name = "display"; + this.display.Size = new System.Drawing.Size(542, 307); + this.display.TabIndex = 5; + this.display.MouseLeave += new System.EventHandler(this.display_MouseLeave); + this.display.Paint += new System.Windows.Forms.PaintEventHandler(this.display_Paint); + this.display.PreviewKeyDown += new System.Windows.Forms.PreviewKeyDownEventHandler(this.display_PreviewKeyDown); + this.display.MouseMove += new System.Windows.Forms.MouseEventHandler(this.display_MouseMove); + this.display.MouseDoubleClick += new System.Windows.Forms.MouseEventHandler(this.display_MouseDoubleClick); + this.display.MouseClick += new System.Windows.Forms.MouseEventHandler(this.display_MouseClick); + this.display.MouseDown += new System.Windows.Forms.MouseEventHandler(this.display_MouseDown); + this.display.Resize += new System.EventHandler(this.display_Resize); + this.display.MouseUp += new System.Windows.Forms.MouseEventHandler(this.display_MouseUp); + this.display.MouseEnter += new System.EventHandler(this.display_MouseEnter); + // + // buttoneditmodesseperator + // + this.buttoneditmodesseperator.Margin = new System.Windows.Forms.Padding(6, 0, 6, 0); + this.buttoneditmodesseperator.Name = "buttoneditmodesseperator"; + this.buttoneditmodesseperator.Size = new System.Drawing.Size(6, 25); + this.buttoneditmodesseperator.Visible = false; + // + // poscommalabel + // + this.poscommalabel.Name = "poscommalabel"; + this.poscommalabel.Size = new System.Drawing.Size(11, 18); + this.poscommalabel.Text = ","; + this.poscommalabel.ToolTipText = "Current X, Y coordinates on map"; + // + // menumain + // + this.menumain.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.menufile, + this.menuedit, + this.menuview, + this.menumode, + this.menuprefabs, + this.menutools, + this.menuhelp}); + this.menumain.Location = new System.Drawing.Point(0, 0); + this.menumain.Name = "menumain"; + this.menumain.Size = new System.Drawing.Size(1012, 24); + this.menumain.TabIndex = 0; + // + // menufile + // + this.menufile.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.itemnewmap, + this.itemopenmap, + this.itemclosemap, + toolStripMenuItem1, + this.itemsavemap, + this.itemsavemapas, + this.itemsavemapinto, + this.toolStripMenuItem5, + this.itemnorecent, + toolStripMenuItem3, + this.itemexit}); + this.menufile.Name = "menufile"; + this.menufile.Size = new System.Drawing.Size(35, 20); + this.menufile.Text = "File"; + // + // itemnewmap + // + this.itemnewmap.Image = global::CodeImp.DoomBuilder.Properties.Resources.File; + this.itemnewmap.Name = "itemnewmap"; + this.itemnewmap.ShortcutKeyDisplayString = ""; + this.itemnewmap.Size = new System.Drawing.Size(201, 22); + this.itemnewmap.Tag = "builder_newmap"; + this.itemnewmap.Text = "New Map"; + this.itemnewmap.Click += new System.EventHandler(this.InvokeTaggedAction); + // + // itemopenmap + // + this.itemopenmap.Image = global::CodeImp.DoomBuilder.Properties.Resources.OpenMap; + this.itemopenmap.Name = "itemopenmap"; + this.itemopenmap.Size = new System.Drawing.Size(201, 22); + this.itemopenmap.Tag = "builder_openmap"; + this.itemopenmap.Text = "Open Map..."; + this.itemopenmap.Click += new System.EventHandler(this.InvokeTaggedAction); + // + // itemclosemap + // + this.itemclosemap.Name = "itemclosemap"; + this.itemclosemap.Size = new System.Drawing.Size(201, 22); + this.itemclosemap.Tag = "builder_closemap"; + this.itemclosemap.Text = "Close Map"; + this.itemclosemap.Click += new System.EventHandler(this.InvokeTaggedAction); + // + // itemsavemap + // + this.itemsavemap.Image = global::CodeImp.DoomBuilder.Properties.Resources.SaveMap; + this.itemsavemap.Name = "itemsavemap"; + this.itemsavemap.Size = new System.Drawing.Size(201, 22); + this.itemsavemap.Tag = "builder_savemap"; + this.itemsavemap.Text = "Save Map"; + this.itemsavemap.Click += new System.EventHandler(this.InvokeTaggedAction); + // + // itemsavemapas + // + this.itemsavemapas.Name = "itemsavemapas"; + this.itemsavemapas.Size = new System.Drawing.Size(201, 22); + this.itemsavemapas.Tag = "builder_savemapas"; + this.itemsavemapas.Text = "Save Map As..."; + this.itemsavemapas.Click += new System.EventHandler(this.InvokeTaggedAction); + // + // itemsavemapinto + // + this.itemsavemapinto.Name = "itemsavemapinto"; + this.itemsavemapinto.Size = new System.Drawing.Size(201, 22); + this.itemsavemapinto.Tag = "builder_savemapinto"; + this.itemsavemapinto.Text = "Save Map Into..."; + this.itemsavemapinto.Click += new System.EventHandler(this.InvokeTaggedAction); + // + // toolStripMenuItem5 + // + this.toolStripMenuItem5.Margin = new System.Windows.Forms.Padding(0, 3, 0, 3); + this.toolStripMenuItem5.Name = "toolStripMenuItem5"; + this.toolStripMenuItem5.Size = new System.Drawing.Size(198, 6); + // + // itemnorecent + // + this.itemnorecent.Enabled = false; + this.itemnorecent.Name = "itemnorecent"; + this.itemnorecent.Size = new System.Drawing.Size(201, 22); + this.itemnorecent.Text = "No recently opened files"; + // + // itemexit + // + this.itemexit.Name = "itemexit"; + this.itemexit.Size = new System.Drawing.Size(201, 22); + this.itemexit.Text = "Exit"; + this.itemexit.Click += new System.EventHandler(this.itemexit_Click); + // + // menuedit + // + this.menuedit.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.itemundo, + this.itemredo, + this.toolStripMenuItem7, + this.itemcut, + this.itemcopy, + this.itempaste, + this.itempastespecial, + toolstripSeperator6, + this.itemsnaptogrid, + this.itemautomerge, + this.itemfullbrightness, + this.toolStripMenuItem6, + this.itemgridinc, + this.itemgriddec, + this.itemgridsetup, + toolStripSeparator11, + this.itemmapoptions}); + this.menuedit.Name = "menuedit"; + this.menuedit.Size = new System.Drawing.Size(37, 20); + this.menuedit.Text = "Edit"; + // + // itemundo + // + this.itemundo.Image = global::CodeImp.DoomBuilder.Properties.Resources.Undo; + this.itemundo.Name = "itemundo"; + this.itemundo.Size = new System.Drawing.Size(165, 22); + this.itemundo.Tag = "builder_undo"; + this.itemundo.Text = "Undo"; + this.itemundo.Click += new System.EventHandler(this.InvokeTaggedAction); + // + // itemredo + // + this.itemredo.Image = global::CodeImp.DoomBuilder.Properties.Resources.Redo; + this.itemredo.Name = "itemredo"; + this.itemredo.Size = new System.Drawing.Size(165, 22); + this.itemredo.Tag = "builder_redo"; + this.itemredo.Text = "Redo"; + this.itemredo.Click += new System.EventHandler(this.InvokeTaggedAction); + // + // toolStripMenuItem7 + // + this.toolStripMenuItem7.Margin = new System.Windows.Forms.Padding(0, 3, 0, 3); + this.toolStripMenuItem7.Name = "toolStripMenuItem7"; + this.toolStripMenuItem7.Size = new System.Drawing.Size(162, 6); + // + // itemcut + // + this.itemcut.Image = global::CodeImp.DoomBuilder.Properties.Resources.Cut; + this.itemcut.Name = "itemcut"; + this.itemcut.Size = new System.Drawing.Size(165, 22); + this.itemcut.Tag = "builder_cutselection"; + this.itemcut.Text = "Cut"; + this.itemcut.Click += new System.EventHandler(this.InvokeTaggedAction); + // + // itemcopy + // + this.itemcopy.Image = global::CodeImp.DoomBuilder.Properties.Resources.Copy; + this.itemcopy.Name = "itemcopy"; + this.itemcopy.Size = new System.Drawing.Size(165, 22); + this.itemcopy.Tag = "builder_copyselection"; + this.itemcopy.Text = "Copy"; + this.itemcopy.Click += new System.EventHandler(this.InvokeTaggedAction); + // + // itempaste + // + this.itempaste.Image = global::CodeImp.DoomBuilder.Properties.Resources.Paste; + this.itempaste.Name = "itempaste"; + this.itempaste.Size = new System.Drawing.Size(165, 22); + this.itempaste.Tag = "builder_pasteselection"; + this.itempaste.Text = "Paste"; + this.itempaste.Click += new System.EventHandler(this.InvokeTaggedAction); + // + // itempastespecial + // + this.itempastespecial.Image = global::CodeImp.DoomBuilder.Properties.Resources.PasteSpecial; + this.itempastespecial.Name = "itempastespecial"; + this.itempastespecial.Size = new System.Drawing.Size(165, 22); + this.itempastespecial.Tag = "builder_pasteselectionspecial"; + this.itempastespecial.Text = "Paste Special..."; + this.itempastespecial.Click += new System.EventHandler(this.InvokeTaggedAction); + // + // itemsnaptogrid + // + this.itemsnaptogrid.Checked = true; + this.itemsnaptogrid.CheckState = System.Windows.Forms.CheckState.Checked; + this.itemsnaptogrid.Image = global::CodeImp.DoomBuilder.Properties.Resources.Grid4; + this.itemsnaptogrid.Name = "itemsnaptogrid"; + this.itemsnaptogrid.Size = new System.Drawing.Size(165, 22); + this.itemsnaptogrid.Tag = "builder_togglesnap"; + this.itemsnaptogrid.Text = "Snap to Grid"; + this.itemsnaptogrid.Click += new System.EventHandler(this.InvokeTaggedAction); + // + // itemautomerge + // + this.itemautomerge.Checked = true; + this.itemautomerge.CheckState = System.Windows.Forms.CheckState.Checked; + this.itemautomerge.Image = global::CodeImp.DoomBuilder.Properties.Resources.mergegeometry2; + this.itemautomerge.Name = "itemautomerge"; + this.itemautomerge.Size = new System.Drawing.Size(165, 22); + this.itemautomerge.Tag = "builder_toggleautomerge"; + this.itemautomerge.Text = "Merge Geometry"; + this.itemautomerge.Click += new System.EventHandler(this.InvokeTaggedAction); + // + // itemfullbrightness + // + this.itemfullbrightness.Checked = true; + this.itemfullbrightness.CheckState = System.Windows.Forms.CheckState.Checked; + this.itemfullbrightness.Image = global::CodeImp.DoomBuilder.Properties.Resources.FullBrightness; + this.itemfullbrightness.Name = "itemfullbrightness"; + this.itemfullbrightness.Size = new System.Drawing.Size(165, 22); + this.itemfullbrightness.Tag = "builder_togglefullbrightness"; + this.itemfullbrightness.Text = "Toggle Full Brightness"; + this.itemfullbrightness.Click += new System.EventHandler(this.InvokeTaggedAction); + // + // toolStripMenuItem6 + // + this.toolStripMenuItem6.Margin = new System.Windows.Forms.Padding(0, 3, 0, 3); + this.toolStripMenuItem6.Name = "toolStripMenuItem6"; + this.toolStripMenuItem6.Size = new System.Drawing.Size(162, 6); + // + // itemgridinc + // + this.itemgridinc.Name = "itemgridinc"; + this.itemgridinc.Size = new System.Drawing.Size(165, 22); + this.itemgridinc.Tag = "builder_griddec"; + this.itemgridinc.Text = "Increase Grid"; + this.itemgridinc.Click += new System.EventHandler(this.InvokeTaggedAction); + // + // itemgriddec + // + this.itemgriddec.Name = "itemgriddec"; + this.itemgriddec.Size = new System.Drawing.Size(165, 22); + this.itemgriddec.Tag = "builder_gridinc"; + this.itemgriddec.Text = "Decrease Grid"; + this.itemgriddec.Click += new System.EventHandler(this.InvokeTaggedAction); + // + // itemgridsetup + // + this.itemgridsetup.Image = global::CodeImp.DoomBuilder.Properties.Resources.Grid2; + this.itemgridsetup.Name = "itemgridsetup"; + this.itemgridsetup.Size = new System.Drawing.Size(165, 22); + this.itemgridsetup.Tag = "builder_gridsetup"; + this.itemgridsetup.Text = "Grid Setup..."; + this.itemgridsetup.Click += new System.EventHandler(this.InvokeTaggedAction); + // + // itemmapoptions + // + this.itemmapoptions.Image = global::CodeImp.DoomBuilder.Properties.Resources.Properties; + this.itemmapoptions.Name = "itemmapoptions"; + this.itemmapoptions.Size = new System.Drawing.Size(165, 22); + this.itemmapoptions.Tag = "builder_mapoptions"; + this.itemmapoptions.Text = "Map Options...."; + this.itemmapoptions.Click += new System.EventHandler(this.InvokeTaggedAction); + // + // menuview + // + this.menuview.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.itemthingsfilter, + this.toolStripMenuItem9, + this.itemviewnormal, + this.itemviewfloors, + this.itemviewceilings, + this.itemviewfloorcolor, + this.itemviewceilingcolor, + this.itemviewthingcolor, + this.toolStripMenuItem2, + this.menuzoom, + this.itemfittoscreen, + this.itemtoggleinfo, + this.toolStripMenuItem10, + this.itemscripteditor}); + this.menuview.Name = "menuview"; + this.menuview.Size = new System.Drawing.Size(41, 20); + this.menuview.Text = "View"; + // + // itemthingsfilter + // + this.itemthingsfilter.Image = global::CodeImp.DoomBuilder.Properties.Resources.Filter; + this.itemthingsfilter.Name = "itemthingsfilter"; + this.itemthingsfilter.Size = new System.Drawing.Size(210, 22); + this.itemthingsfilter.Tag = "builder_thingsfilterssetup"; + this.itemthingsfilter.Text = "Configure Things Filters..."; + this.itemthingsfilter.Click += new System.EventHandler(this.InvokeTaggedAction); + // + // toolStripMenuItem9 + // + this.toolStripMenuItem9.Margin = new System.Windows.Forms.Padding(0, 3, 0, 3); + this.toolStripMenuItem9.Name = "toolStripMenuItem9"; + this.toolStripMenuItem9.Size = new System.Drawing.Size(207, 6); + // + // itemviewnormal + // + this.itemviewnormal.Image = global::CodeImp.DoomBuilder.Properties.Resources.ViewNormal; + this.itemviewnormal.Name = "itemviewnormal"; + this.itemviewnormal.Size = new System.Drawing.Size(210, 22); + this.itemviewnormal.Tag = "builder_viewmodenormal"; + this.itemviewnormal.Text = "Wireframe"; + this.itemviewnormal.Click += new System.EventHandler(this.InvokeTaggedAction); + // + // itemviewfloors + // + this.itemviewfloors.Image = global::CodeImp.DoomBuilder.Properties.Resources.ViewTextureFloor; + this.itemviewfloors.Name = "itemviewfloors"; + this.itemviewfloors.Size = new System.Drawing.Size(210, 22); + this.itemviewfloors.Tag = "builder_viewmodefloors"; + this.itemviewfloors.Text = "Floor Textures"; + this.itemviewfloors.Click += new System.EventHandler(this.InvokeTaggedAction); + // + // itemviewceilings + // + this.itemviewceilings.Image = global::CodeImp.DoomBuilder.Properties.Resources.ViewTextureCeiling; + this.itemviewceilings.Name = "itemviewceilings"; + this.itemviewceilings.Size = new System.Drawing.Size(210, 22); + this.itemviewceilings.Tag = "builder_viewmodeceilings"; + this.itemviewceilings.Text = "Ceiling Textures"; + this.itemviewceilings.Click += new System.EventHandler(this.InvokeTaggedAction); + // + // itemviewfloorcolor + // + this.itemviewfloorcolor.Image = global::CodeImp.DoomBuilder.Properties.Resources.ViewColorFloor; + this.itemviewfloorcolor.Name = "itemviewfloorcolor"; + this.itemviewfloorcolor.Size = new System.Drawing.Size(210, 22); + this.itemviewfloorcolor.Tag = "builder_viewmodefloorcolor"; + this.itemviewfloorcolor.Text = "Floor Color"; + this.itemviewfloorcolor.Click += new System.EventHandler(this.InvokeTaggedAction); + // + // itemviewceilingcolor + // + this.itemviewceilingcolor.Image = global::CodeImp.DoomBuilder.Properties.Resources.ViewColorCeiling; + this.itemviewceilingcolor.Name = "itemviewceilingcolor"; + this.itemviewceilingcolor.Size = new System.Drawing.Size(210, 22); + this.itemviewceilingcolor.Tag = "builder_viewmodeceilingcolor"; + this.itemviewceilingcolor.Text = "Ceiling Color"; + this.itemviewceilingcolor.Click += new System.EventHandler(this.InvokeTaggedAction); + // + // itemviewthingcolor + // + this.itemviewthingcolor.Image = global::CodeImp.DoomBuilder.Properties.Resources.ViewColorThing; + this.itemviewthingcolor.Name = "itemviewthingcolor"; + this.itemviewthingcolor.Size = new System.Drawing.Size(210, 22); + this.itemviewthingcolor.Tag = "builder_viewmodethingcolor"; + this.itemviewthingcolor.Text = "Thing Color"; + this.itemviewthingcolor.Click += new System.EventHandler(this.InvokeTaggedAction); + // + // toolStripMenuItem2 + // + this.toolStripMenuItem2.Name = "toolStripMenuItem2"; + this.toolStripMenuItem2.Size = new System.Drawing.Size(207, 6); + // + // menuzoom + // + this.menuzoom.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.item2zoom200, + this.item2zoom100, + this.item2zoom50, + this.item2zoom25, + this.item2zoom10, + this.item2zoom5}); + this.menuzoom.Image = global::CodeImp.DoomBuilder.Properties.Resources.Zoom; + this.menuzoom.Name = "menuzoom"; + this.menuzoom.Size = new System.Drawing.Size(210, 22); + this.menuzoom.Text = "Zoom"; + // + // item2zoom200 + // + this.item2zoom200.Name = "item2zoom200"; + this.item2zoom200.Size = new System.Drawing.Size(114, 22); + this.item2zoom200.Tag = "200"; + this.item2zoom200.Text = "200%"; + this.item2zoom200.Click += new System.EventHandler(this.itemzoomto_Click); + // + // item2zoom100 + // + this.item2zoom100.Name = "item2zoom100"; + this.item2zoom100.Size = new System.Drawing.Size(114, 22); + this.item2zoom100.Tag = "100"; + this.item2zoom100.Text = "100%"; + this.item2zoom100.Click += new System.EventHandler(this.itemzoomto_Click); + // + // item2zoom50 + // + this.item2zoom50.Name = "item2zoom50"; + this.item2zoom50.Size = new System.Drawing.Size(114, 22); + this.item2zoom50.Tag = "50"; + this.item2zoom50.Text = "50%"; + this.item2zoom50.Click += new System.EventHandler(this.itemzoomto_Click); + // + // item2zoom25 + // + this.item2zoom25.Name = "item2zoom25"; + this.item2zoom25.Size = new System.Drawing.Size(114, 22); + this.item2zoom25.Tag = "25"; + this.item2zoom25.Text = "25%"; + this.item2zoom25.Click += new System.EventHandler(this.itemzoomto_Click); + // + // item2zoom10 + // + this.item2zoom10.Name = "item2zoom10"; + this.item2zoom10.Size = new System.Drawing.Size(114, 22); + this.item2zoom10.Tag = "10"; + this.item2zoom10.Text = "10%"; + this.item2zoom10.Click += new System.EventHandler(this.itemzoomto_Click); + // + // item2zoom5 + // + this.item2zoom5.Name = "item2zoom5"; + this.item2zoom5.Size = new System.Drawing.Size(114, 22); + this.item2zoom5.Tag = "5"; + this.item2zoom5.Text = "5%"; + this.item2zoom5.Click += new System.EventHandler(this.itemzoomto_Click); + // + // itemfittoscreen + // + this.itemfittoscreen.Name = "itemfittoscreen"; + this.itemfittoscreen.Size = new System.Drawing.Size(210, 22); + this.itemfittoscreen.Tag = "builder_centerinscreen"; + this.itemfittoscreen.Text = "Fit to screen"; + this.itemfittoscreen.Click += new System.EventHandler(this.InvokeTaggedAction); + // + // itemtoggleinfo + // + this.itemtoggleinfo.Name = "itemtoggleinfo"; + this.itemtoggleinfo.Size = new System.Drawing.Size(210, 22); + this.itemtoggleinfo.Tag = "builder_toggleinfopanel"; + this.itemtoggleinfo.Text = "Expanded Info Panel"; + this.itemtoggleinfo.Click += new System.EventHandler(this.InvokeTaggedAction); + // + // toolStripMenuItem10 + // + this.toolStripMenuItem10.Margin = new System.Windows.Forms.Padding(0, 3, 0, 3); + this.toolStripMenuItem10.Name = "toolStripMenuItem10"; + this.toolStripMenuItem10.Size = new System.Drawing.Size(207, 6); + // + // itemscripteditor + // + this.itemscripteditor.Image = global::CodeImp.DoomBuilder.Properties.Resources.Script2; + this.itemscripteditor.Name = "itemscripteditor"; + this.itemscripteditor.Size = new System.Drawing.Size(210, 22); + this.itemscripteditor.Tag = "builder_openscripteditor"; + this.itemscripteditor.Text = "Script Editor..."; + this.itemscripteditor.Click += new System.EventHandler(this.InvokeTaggedAction); + // + // menumode + // + this.menumode.Name = "menumode"; + this.menumode.Size = new System.Drawing.Size(45, 20); + this.menumode.Text = "Mode"; + // + // menuprefabs + // + this.menuprefabs.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.iteminsertprefabfile, + this.iteminsertpreviousprefab, + this.toolStripMenuItem12, + this.itemcreateprefab}); + this.menuprefabs.Name = "menuprefabs"; + this.menuprefabs.Size = new System.Drawing.Size(56, 20); + this.menuprefabs.Text = "Prefabs"; + // + // iteminsertprefabfile + // + this.iteminsertprefabfile.Name = "iteminsertprefabfile"; + this.iteminsertprefabfile.Size = new System.Drawing.Size(205, 22); + this.iteminsertprefabfile.Tag = "builder_insertprefabfile"; + this.iteminsertprefabfile.Text = "Insert Prefab from File..."; + this.iteminsertprefabfile.Click += new System.EventHandler(this.InvokeTaggedAction); + // + // iteminsertpreviousprefab + // + this.iteminsertpreviousprefab.Name = "iteminsertpreviousprefab"; + this.iteminsertpreviousprefab.Size = new System.Drawing.Size(205, 22); + this.iteminsertpreviousprefab.Tag = "builder_insertpreviousprefab"; + this.iteminsertpreviousprefab.Text = "Insert Previous Prefab"; + this.iteminsertpreviousprefab.Click += new System.EventHandler(this.InvokeTaggedAction); + // + // toolStripMenuItem12 + // + this.toolStripMenuItem12.Margin = new System.Windows.Forms.Padding(0, 3, 0, 3); + this.toolStripMenuItem12.Name = "toolStripMenuItem12"; + this.toolStripMenuItem12.Size = new System.Drawing.Size(202, 6); + // + // itemcreateprefab + // + this.itemcreateprefab.Name = "itemcreateprefab"; + this.itemcreateprefab.Size = new System.Drawing.Size(205, 22); + this.itemcreateprefab.Tag = "builder_createprefab"; + this.itemcreateprefab.Text = "Create From Selection..."; + this.itemcreateprefab.Click += new System.EventHandler(this.InvokeTaggedAction); + // + // menutools + // + this.menutools.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.itemreloadresources, + this.itemshowerrors, + this.toolStripMenuItem8, + this.configurationToolStripMenuItem, + this.preferencesToolStripMenuItem, + this.toolStripMenuItem11, + this.itemtestmap}); + this.menutools.Name = "menutools"; + this.menutools.Size = new System.Drawing.Size(44, 20); + this.menutools.Text = "Tools"; + // + // itemreloadresources + // + this.itemreloadresources.Name = "itemreloadresources"; + this.itemreloadresources.Size = new System.Drawing.Size(197, 22); + this.itemreloadresources.Tag = "builder_reloadresources"; + this.itemreloadresources.Text = "Reload Resources"; + this.itemreloadresources.Click += new System.EventHandler(this.InvokeTaggedAction); + // + // itemshowerrors + // + this.itemshowerrors.Image = global::CodeImp.DoomBuilder.Properties.Resources.Warning; + this.itemshowerrors.Name = "itemshowerrors"; + this.itemshowerrors.Size = new System.Drawing.Size(197, 22); + this.itemshowerrors.Tag = "builder_showerrors"; + this.itemshowerrors.Text = "Errors and Warnings..."; + this.itemshowerrors.Click += new System.EventHandler(this.InvokeTaggedAction); + // + // toolStripMenuItem8 + // + this.toolStripMenuItem8.Margin = new System.Windows.Forms.Padding(0, 3, 0, 3); + this.toolStripMenuItem8.Name = "toolStripMenuItem8"; + this.toolStripMenuItem8.Size = new System.Drawing.Size(194, 6); + // + // configurationToolStripMenuItem + // + this.configurationToolStripMenuItem.Name = "configurationToolStripMenuItem"; + this.configurationToolStripMenuItem.Size = new System.Drawing.Size(197, 22); + this.configurationToolStripMenuItem.Tag = "builder_configuration"; + this.configurationToolStripMenuItem.Text = "Game Configurations..."; + this.configurationToolStripMenuItem.Click += new System.EventHandler(this.InvokeTaggedAction); + // + // preferencesToolStripMenuItem + // + this.preferencesToolStripMenuItem.Name = "preferencesToolStripMenuItem"; + this.preferencesToolStripMenuItem.Size = new System.Drawing.Size(197, 22); + this.preferencesToolStripMenuItem.Tag = "builder_preferences"; + this.preferencesToolStripMenuItem.Text = "Preferences..."; + this.preferencesToolStripMenuItem.Click += new System.EventHandler(this.InvokeTaggedAction); + // + // toolStripMenuItem11 + // + this.toolStripMenuItem11.Margin = new System.Windows.Forms.Padding(0, 3, 0, 3); + this.toolStripMenuItem11.Name = "toolStripMenuItem11"; + this.toolStripMenuItem11.Size = new System.Drawing.Size(194, 6); + // + // itemtestmap + // + this.itemtestmap.Image = global::CodeImp.DoomBuilder.Properties.Resources.Test; + this.itemtestmap.Name = "itemtestmap"; + this.itemtestmap.Size = new System.Drawing.Size(197, 22); + this.itemtestmap.Tag = "builder_testmap"; + this.itemtestmap.Text = "Test Map"; + this.itemtestmap.Click += new System.EventHandler(this.InvokeTaggedAction); + // + // menuhelp + // + this.menuhelp.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.itemhelprefmanual, + this.itemhelpeditmode, + this.toolStripMenuItem13, + this.itemhelpabout}); + this.menuhelp.Name = "menuhelp"; + this.menuhelp.Size = new System.Drawing.Size(40, 20); + this.menuhelp.Text = "Help"; + // + // itemhelprefmanual + // + this.itemhelprefmanual.Image = global::CodeImp.DoomBuilder.Properties.Resources.Help; + this.itemhelprefmanual.Name = "itemhelprefmanual"; + this.itemhelprefmanual.Size = new System.Drawing.Size(198, 22); + this.itemhelprefmanual.Text = "Reference Manual"; + this.itemhelprefmanual.Click += new System.EventHandler(this.itemhelprefmanual_Click); + // + // itemhelpeditmode + // + this.itemhelpeditmode.Image = global::CodeImp.DoomBuilder.Properties.Resources.Question; + this.itemhelpeditmode.Name = "itemhelpeditmode"; + this.itemhelpeditmode.Size = new System.Drawing.Size(198, 22); + this.itemhelpeditmode.Text = "About this Editing Mode"; + this.itemhelpeditmode.Click += new System.EventHandler(this.itemhelpeditmode_Click); + // + // toolStripMenuItem13 + // + this.toolStripMenuItem13.Margin = new System.Windows.Forms.Padding(0, 3, 0, 3); + this.toolStripMenuItem13.Name = "toolStripMenuItem13"; + this.toolStripMenuItem13.Size = new System.Drawing.Size(195, 6); + // + // itemhelpabout + // + this.itemhelpabout.Name = "itemhelpabout"; + this.itemhelpabout.Size = new System.Drawing.Size(198, 22); + this.itemhelpabout.Text = "About Doom Builder..."; + this.itemhelpabout.Click += new System.EventHandler(this.itemhelpabout_Click); + // + // toolbar + // + this.toolbar.AutoSize = false; + this.toolbar.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden; + this.toolbar.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.buttonnewmap, + this.buttonopenmap, + this.buttonsavemap, + toolStripSeparator3, + this.buttonmapoptions, + this.buttonscripteditor, + toolStripSeparator10, + this.buttonundo, + this.buttonredo, + toolStripSeparator7, + this.buttoncut, + this.buttoncopy, + this.buttonpaste, + toolStripSeparator4, + this.buttoninsertprefabfile, + this.buttoninsertpreviousprefab, + toolstripSeperator1, + this.buttoneditmodesseperator, + this.buttonthingsfilter, + this.thingfilters, + this.buttonviewnormal, + this.buttonviewfloors, + this.buttonviewceilings, + this.buttonviewfloorcolor, + this.buttonviewceilingcolor, + this.buttonviewthingcolor, + this.toolStripSeparator8, + this.buttonsnaptogrid, + this.buttonautomerge, + this.buttonfullbrightness, + this.toolStripSeparator5, + this.buttontest, + this.toolStripSeparator6}); + this.toolbar.Location = new System.Drawing.Point(0, 24); + this.toolbar.Name = "toolbar"; + this.toolbar.Size = new System.Drawing.Size(1012, 25); + this.toolbar.TabIndex = 1; + // + // buttonnewmap + // + this.buttonnewmap.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this.buttonnewmap.Image = global::CodeImp.DoomBuilder.Properties.Resources.NewMap; + this.buttonnewmap.ImageTransparentColor = System.Drawing.Color.Magenta; + this.buttonnewmap.Margin = new System.Windows.Forms.Padding(6, 1, 0, 2); + this.buttonnewmap.Name = "buttonnewmap"; + this.buttonnewmap.Size = new System.Drawing.Size(23, 22); + this.buttonnewmap.Tag = "builder_newmap"; + this.buttonnewmap.Text = "New Map"; + this.buttonnewmap.Click += new System.EventHandler(this.InvokeTaggedAction); + // + // buttonopenmap + // + this.buttonopenmap.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this.buttonopenmap.Image = global::CodeImp.DoomBuilder.Properties.Resources.OpenMap; + this.buttonopenmap.ImageTransparentColor = System.Drawing.Color.Magenta; + this.buttonopenmap.Name = "buttonopenmap"; + this.buttonopenmap.Size = new System.Drawing.Size(23, 22); + this.buttonopenmap.Tag = "builder_openmap"; + this.buttonopenmap.Text = "Open Map"; + this.buttonopenmap.Click += new System.EventHandler(this.InvokeTaggedAction); + // + // buttonsavemap + // + this.buttonsavemap.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this.buttonsavemap.Image = global::CodeImp.DoomBuilder.Properties.Resources.SaveMap; + this.buttonsavemap.ImageTransparentColor = System.Drawing.Color.Magenta; + this.buttonsavemap.Name = "buttonsavemap"; + this.buttonsavemap.Size = new System.Drawing.Size(23, 22); + this.buttonsavemap.Tag = "builder_savemap"; + this.buttonsavemap.Text = "Save Map"; + this.buttonsavemap.Click += new System.EventHandler(this.InvokeTaggedAction); + // + // buttonmapoptions + // + this.buttonmapoptions.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this.buttonmapoptions.Image = global::CodeImp.DoomBuilder.Properties.Resources.Properties; + this.buttonmapoptions.ImageTransparentColor = System.Drawing.Color.Magenta; + this.buttonmapoptions.Name = "buttonmapoptions"; + this.buttonmapoptions.Size = new System.Drawing.Size(23, 22); + this.buttonmapoptions.Tag = "builder_mapoptions"; + this.buttonmapoptions.Text = "Map Options"; + this.buttonmapoptions.Click += new System.EventHandler(this.InvokeTaggedAction); + // + // buttonscripteditor + // + this.buttonscripteditor.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this.buttonscripteditor.Image = global::CodeImp.DoomBuilder.Properties.Resources.Script2; + this.buttonscripteditor.ImageTransparentColor = System.Drawing.Color.Magenta; + this.buttonscripteditor.Name = "buttonscripteditor"; + this.buttonscripteditor.Size = new System.Drawing.Size(23, 22); + this.buttonscripteditor.Tag = "builder_openscripteditor"; + this.buttonscripteditor.Text = "Open Script Editor"; + this.buttonscripteditor.Click += new System.EventHandler(this.InvokeTaggedAction); + // + // buttonundo + // + this.buttonundo.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this.buttonundo.Image = global::CodeImp.DoomBuilder.Properties.Resources.Undo; + this.buttonundo.ImageTransparentColor = System.Drawing.Color.Magenta; + this.buttonundo.Name = "buttonundo"; + this.buttonundo.Size = new System.Drawing.Size(23, 22); + this.buttonundo.Tag = "builder_undo"; + this.buttonundo.Text = "Undo"; + this.buttonundo.Click += new System.EventHandler(this.InvokeTaggedAction); + // + // buttonredo + // + this.buttonredo.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this.buttonredo.Image = global::CodeImp.DoomBuilder.Properties.Resources.Redo; + this.buttonredo.ImageTransparentColor = System.Drawing.Color.Magenta; + this.buttonredo.Name = "buttonredo"; + this.buttonredo.Size = new System.Drawing.Size(23, 22); + this.buttonredo.Tag = "builder_redo"; + this.buttonredo.Text = "Redo"; + this.buttonredo.Click += new System.EventHandler(this.InvokeTaggedAction); + // + // buttoncut + // + this.buttoncut.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this.buttoncut.Image = global::CodeImp.DoomBuilder.Properties.Resources.Cut; + this.buttoncut.ImageTransparentColor = System.Drawing.Color.Magenta; + this.buttoncut.Name = "buttoncut"; + this.buttoncut.Size = new System.Drawing.Size(23, 22); + this.buttoncut.Tag = "builder_cutselection"; + this.buttoncut.Text = "Cut Selection"; + this.buttoncut.Click += new System.EventHandler(this.InvokeTaggedAction); + // + // buttoncopy + // + this.buttoncopy.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this.buttoncopy.Image = global::CodeImp.DoomBuilder.Properties.Resources.Copy; + this.buttoncopy.ImageTransparentColor = System.Drawing.Color.Magenta; + this.buttoncopy.Name = "buttoncopy"; + this.buttoncopy.Size = new System.Drawing.Size(23, 22); + this.buttoncopy.Tag = "builder_copyselection"; + this.buttoncopy.Text = "Copy Selection"; + this.buttoncopy.Click += new System.EventHandler(this.InvokeTaggedAction); + // + // buttonpaste + // + this.buttonpaste.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this.buttonpaste.Image = global::CodeImp.DoomBuilder.Properties.Resources.Paste; + this.buttonpaste.ImageTransparentColor = System.Drawing.Color.Magenta; + this.buttonpaste.Name = "buttonpaste"; + this.buttonpaste.Size = new System.Drawing.Size(23, 22); + this.buttonpaste.Tag = "builder_pasteselection"; + this.buttonpaste.Text = "Paste Selection"; + this.buttonpaste.Click += new System.EventHandler(this.InvokeTaggedAction); + // + // buttoninsertprefabfile + // + this.buttoninsertprefabfile.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this.buttoninsertprefabfile.Image = global::CodeImp.DoomBuilder.Properties.Resources.Prefab; + this.buttoninsertprefabfile.ImageTransparentColor = System.Drawing.Color.Magenta; + this.buttoninsertprefabfile.Name = "buttoninsertprefabfile"; + this.buttoninsertprefabfile.Size = new System.Drawing.Size(23, 22); + this.buttoninsertprefabfile.Tag = "builder_insertprefabfile"; + this.buttoninsertprefabfile.Text = "Insert Prefab from File"; + this.buttoninsertprefabfile.Click += new System.EventHandler(this.InvokeTaggedAction); + // + // buttoninsertpreviousprefab + // + this.buttoninsertpreviousprefab.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this.buttoninsertpreviousprefab.Image = global::CodeImp.DoomBuilder.Properties.Resources.Prefab2; + this.buttoninsertpreviousprefab.ImageTransparentColor = System.Drawing.Color.Magenta; + this.buttoninsertpreviousprefab.Name = "buttoninsertpreviousprefab"; + this.buttoninsertpreviousprefab.Size = new System.Drawing.Size(23, 22); + this.buttoninsertpreviousprefab.Tag = "builder_insertpreviousprefab"; + this.buttoninsertpreviousprefab.Text = "Insert Previous Prefab"; + this.buttoninsertpreviousprefab.Click += new System.EventHandler(this.InvokeTaggedAction); + // + // buttonthingsfilter + // + this.buttonthingsfilter.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this.buttonthingsfilter.Enabled = false; + this.buttonthingsfilter.Image = global::CodeImp.DoomBuilder.Properties.Resources.Filter; + this.buttonthingsfilter.ImageTransparentColor = System.Drawing.Color.Magenta; + this.buttonthingsfilter.Name = "buttonthingsfilter"; + this.buttonthingsfilter.Size = new System.Drawing.Size(23, 22); + this.buttonthingsfilter.Tag = "builder_thingsfilterssetup"; + this.buttonthingsfilter.Text = "Configure Things Filters"; + this.buttonthingsfilter.Click += new System.EventHandler(this.InvokeTaggedAction); + // + // thingfilters + // + this.thingfilters.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.thingfilters.Enabled = false; + this.thingfilters.Items.AddRange(new object[] { + "(none)", + "(custom)", + "Easy skill items only", + "Medium skill items only", + "Hard skill items only"}); + this.thingfilters.Margin = new System.Windows.Forms.Padding(1, 0, 6, 0); + this.thingfilters.Name = "thingfilters"; + this.thingfilters.Size = new System.Drawing.Size(130, 25); + this.thingfilters.ToolTipText = "Things Filter"; + this.thingfilters.SelectedIndexChanged += new System.EventHandler(this.thingfilters_SelectedIndexChanged); + this.thingfilters.DropDownClosed += new System.EventHandler(this.LoseFocus); + // + // buttonviewnormal + // + this.buttonviewnormal.CheckOnClick = true; + this.buttonviewnormal.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this.buttonviewnormal.Image = global::CodeImp.DoomBuilder.Properties.Resources.ViewNormal; + this.buttonviewnormal.ImageTransparentColor = System.Drawing.Color.Magenta; + this.buttonviewnormal.Name = "buttonviewnormal"; + this.buttonviewnormal.Size = new System.Drawing.Size(23, 22); + this.buttonviewnormal.Tag = "builder_viewmodenormal"; + this.buttonviewnormal.Text = "View Wireframe"; + this.buttonviewnormal.Click += new System.EventHandler(this.InvokeTaggedAction); + // + // buttonviewfloors + // + this.buttonviewfloors.CheckOnClick = true; + this.buttonviewfloors.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this.buttonviewfloors.Image = global::CodeImp.DoomBuilder.Properties.Resources.ViewTextureFloor; + this.buttonviewfloors.ImageTransparentColor = System.Drawing.Color.Magenta; + this.buttonviewfloors.Name = "buttonviewfloors"; + this.buttonviewfloors.Size = new System.Drawing.Size(23, 22); + this.buttonviewfloors.Tag = "builder_viewmodefloors"; + this.buttonviewfloors.Text = "View Floor Textures"; + this.buttonviewfloors.Click += new System.EventHandler(this.InvokeTaggedAction); + // + // buttonviewceilings + // + this.buttonviewceilings.CheckOnClick = true; + this.buttonviewceilings.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this.buttonviewceilings.Image = global::CodeImp.DoomBuilder.Properties.Resources.ViewTextureCeiling; + this.buttonviewceilings.ImageTransparentColor = System.Drawing.Color.Magenta; + this.buttonviewceilings.Name = "buttonviewceilings"; + this.buttonviewceilings.Size = new System.Drawing.Size(23, 22); + this.buttonviewceilings.Tag = "builder_viewmodeceilings"; + this.buttonviewceilings.Text = "View Ceiling Textures"; + this.buttonviewceilings.Click += new System.EventHandler(this.InvokeTaggedAction); + // + // buttonviewfloorcolor + // + this.buttonviewfloorcolor.CheckOnClick = true; + this.buttonviewfloorcolor.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this.buttonviewfloorcolor.Image = global::CodeImp.DoomBuilder.Properties.Resources.ViewColorFloor; + this.buttonviewfloorcolor.ImageTransparentColor = System.Drawing.Color.Magenta; + this.buttonviewfloorcolor.Name = "buttonviewfloorcolor"; + this.buttonviewfloorcolor.Size = new System.Drawing.Size(23, 22); + this.buttonviewfloorcolor.Tag = "builder_viewmodefloorcolor"; + this.buttonviewfloorcolor.Text = "View Floor Color"; + this.buttonviewfloorcolor.Click += new System.EventHandler(this.InvokeTaggedAction); + // + // buttonviewceilingcolor + // + this.buttonviewceilingcolor.CheckOnClick = true; + this.buttonviewceilingcolor.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this.buttonviewceilingcolor.Image = global::CodeImp.DoomBuilder.Properties.Resources.ViewColorCeiling; + this.buttonviewceilingcolor.ImageTransparentColor = System.Drawing.Color.Magenta; + this.buttonviewceilingcolor.Name = "buttonviewceilingcolor"; + this.buttonviewceilingcolor.Size = new System.Drawing.Size(23, 22); + this.buttonviewceilingcolor.Tag = "builder_viewmodeceilingcolor"; + this.buttonviewceilingcolor.Text = "View Ceiling Color"; + this.buttonviewceilingcolor.Click += new System.EventHandler(this.InvokeTaggedAction); + // + // buttonviewthingcolor + // + this.buttonviewthingcolor.CheckOnClick = true; + this.buttonviewthingcolor.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this.buttonviewthingcolor.Image = global::CodeImp.DoomBuilder.Properties.Resources.ViewColorThing; + this.buttonviewthingcolor.ImageTransparentColor = System.Drawing.Color.Magenta; + this.buttonviewthingcolor.Name = "buttonviewthingcolor"; + this.buttonviewthingcolor.Size = new System.Drawing.Size(23, 22); + this.buttonviewthingcolor.Tag = "builder_viewmodethingcolor"; + this.buttonviewthingcolor.Text = "View Thing Color"; + this.buttonviewthingcolor.Click += new System.EventHandler(this.InvokeTaggedAction); + // + // toolStripSeparator8 + // + this.toolStripSeparator8.Margin = new System.Windows.Forms.Padding(6, 0, 6, 0); + this.toolStripSeparator8.Name = "toolStripSeparator8"; + this.toolStripSeparator8.Size = new System.Drawing.Size(6, 25); + // + // buttonsnaptogrid + // + this.buttonsnaptogrid.Checked = true; + this.buttonsnaptogrid.CheckState = System.Windows.Forms.CheckState.Checked; + this.buttonsnaptogrid.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this.buttonsnaptogrid.Image = global::CodeImp.DoomBuilder.Properties.Resources.Grid4; + this.buttonsnaptogrid.ImageTransparentColor = System.Drawing.Color.Magenta; + this.buttonsnaptogrid.Name = "buttonsnaptogrid"; + this.buttonsnaptogrid.Size = new System.Drawing.Size(23, 22); + this.buttonsnaptogrid.Tag = "builder_togglesnap"; + this.buttonsnaptogrid.Text = "Snap to Grid"; + this.buttonsnaptogrid.Click += new System.EventHandler(this.InvokeTaggedAction); + // + // buttonautomerge + // + this.buttonautomerge.Checked = true; + this.buttonautomerge.CheckState = System.Windows.Forms.CheckState.Checked; + this.buttonautomerge.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this.buttonautomerge.Image = global::CodeImp.DoomBuilder.Properties.Resources.mergegeometry2; + this.buttonautomerge.ImageTransparentColor = System.Drawing.Color.Magenta; + this.buttonautomerge.Name = "buttonautomerge"; + this.buttonautomerge.Size = new System.Drawing.Size(23, 22); + this.buttonautomerge.Tag = "builder_toggleautomerge"; + this.buttonautomerge.Text = "Merge Geometry"; + this.buttonautomerge.Click += new System.EventHandler(this.InvokeTaggedAction); + // + // buttonfullbrightness + // + this.buttonfullbrightness.Checked = true; + this.buttonfullbrightness.CheckState = System.Windows.Forms.CheckState.Checked; + this.buttonfullbrightness.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this.buttonfullbrightness.Image = global::CodeImp.DoomBuilder.Properties.Resources.FullBrightness; + this.buttonfullbrightness.ImageTransparentColor = System.Drawing.Color.Magenta; + this.buttonfullbrightness.Name = "buttonfullbrightness"; + this.buttonfullbrightness.Size = new System.Drawing.Size(23, 22); + this.buttonfullbrightness.Tag = "builder_togglefullbrightness"; + this.buttonfullbrightness.Text = "Toggle Full Brightness"; + this.buttonfullbrightness.Click += new System.EventHandler(this.InvokeTaggedAction); + // + // toolStripSeparator5 + // + this.toolStripSeparator5.Margin = new System.Windows.Forms.Padding(6, 0, 6, 0); + this.toolStripSeparator5.Name = "toolStripSeparator5"; + this.toolStripSeparator5.Size = new System.Drawing.Size(6, 25); + // + // buttontest + // + this.buttontest.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this.buttontest.Image = global::CodeImp.DoomBuilder.Properties.Resources.Test; + this.buttontest.ImageScaling = System.Windows.Forms.ToolStripItemImageScaling.None; + this.buttontest.ImageTransparentColor = System.Drawing.Color.Magenta; + this.buttontest.Name = "buttontest"; + this.buttontest.Size = new System.Drawing.Size(32, 22); + this.buttontest.Tag = "builder_testmap"; + this.buttontest.Text = "Test Map"; + this.buttontest.ButtonClick += new System.EventHandler(this.InvokeTaggedAction); + // + // toolStripSeparator6 + // + this.toolStripSeparator6.Margin = new System.Windows.Forms.Padding(6, 0, 6, 0); + this.toolStripSeparator6.Name = "toolStripSeparator6"; + this.toolStripSeparator6.Size = new System.Drawing.Size(6, 25); + // + // statusbar + // + this.statusbar.Font = new System.Drawing.Font("Verdana", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.statusbar.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.statuslabel, + this.configlabel, + toolStripSeparator12, + this.gridlabel, + this.buttongrid, + toolStripSeparator1, + this.zoomlabel, + this.buttonzoom, + toolStripSeparator9, + this.xposlabel, + this.poscommalabel, + this.yposlabel}); + this.statusbar.Location = new System.Drawing.Point(0, 670); + this.statusbar.Name = "statusbar"; + this.statusbar.ShowItemToolTips = true; + this.statusbar.Size = new System.Drawing.Size(1012, 23); + this.statusbar.TabIndex = 2; + // + // statuslabel + // + this.statuslabel.Image = global::CodeImp.DoomBuilder.Properties.Resources.Status2; + this.statuslabel.ImageAlign = System.Drawing.ContentAlignment.MiddleLeft; + this.statuslabel.ImageScaling = System.Windows.Forms.ToolStripItemImageScaling.None; + this.statuslabel.Name = "statuslabel"; + this.statuslabel.Size = new System.Drawing.Size(396, 18); + this.statuslabel.Spring = true; + this.statuslabel.Text = "Initializing user interface..."; + this.statuslabel.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; + // + // configlabel + // + this.configlabel.AutoSize = false; + this.configlabel.Font = new System.Drawing.Font("Verdana", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.configlabel.Name = "configlabel"; + this.configlabel.Size = new System.Drawing.Size(280, 18); + this.configlabel.Text = "ZDoom (Doom in Hexen Format)"; + this.configlabel.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + this.configlabel.ToolTipText = "Current Game Configuration"; + // + // gridlabel + // + this.gridlabel.AutoSize = false; + this.gridlabel.AutoToolTip = true; + this.gridlabel.Name = "gridlabel"; + this.gridlabel.Size = new System.Drawing.Size(62, 18); + this.gridlabel.Text = "32 mp"; + this.gridlabel.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + this.gridlabel.TextImageRelation = System.Windows.Forms.TextImageRelation.Overlay; + this.gridlabel.ToolTipText = "Grid size"; + // + // buttongrid + // + this.buttongrid.AutoToolTip = false; + this.buttongrid.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this.buttongrid.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.itemgrid1024, + this.itemgrid512, + this.itemgrid256, + this.itemgrid128, + this.itemgrid64, + this.itemgrid32, + this.itemgrid16, + this.itemgrid8, + this.itemgrid4, + toolStripMenuItem4, + this.itemgridcustom}); + this.buttongrid.Image = global::CodeImp.DoomBuilder.Properties.Resources.Grid2_arrowup; + this.buttongrid.ImageScaling = System.Windows.Forms.ToolStripItemImageScaling.None; + this.buttongrid.ImageTransparentColor = System.Drawing.Color.Transparent; + this.buttongrid.Name = "buttongrid"; + this.buttongrid.ShowDropDownArrow = false; + this.buttongrid.Size = new System.Drawing.Size(29, 21); + this.buttongrid.Text = "Grid"; + // + // itemgrid1024 + // + this.itemgrid1024.Name = "itemgrid1024"; + this.itemgrid1024.Size = new System.Drawing.Size(164, 22); + this.itemgrid1024.Tag = "1024"; + this.itemgrid1024.Text = "1024 mp"; + this.itemgrid1024.Click += new System.EventHandler(this.itemgridsize_Click); + // + // itemgrid512 + // + this.itemgrid512.Name = "itemgrid512"; + this.itemgrid512.Size = new System.Drawing.Size(164, 22); + this.itemgrid512.Tag = "512"; + this.itemgrid512.Text = "512 mp"; + this.itemgrid512.Click += new System.EventHandler(this.itemgridsize_Click); + // + // itemgrid256 + // + this.itemgrid256.Name = "itemgrid256"; + this.itemgrid256.Size = new System.Drawing.Size(164, 22); + this.itemgrid256.Tag = "256"; + this.itemgrid256.Text = "256 mp"; + this.itemgrid256.Click += new System.EventHandler(this.itemgridsize_Click); + // + // itemgrid128 + // + this.itemgrid128.Name = "itemgrid128"; + this.itemgrid128.Size = new System.Drawing.Size(164, 22); + this.itemgrid128.Tag = "128"; + this.itemgrid128.Text = "128 mp"; + this.itemgrid128.Click += new System.EventHandler(this.itemgridsize_Click); + // + // itemgrid64 + // + this.itemgrid64.Name = "itemgrid64"; + this.itemgrid64.Size = new System.Drawing.Size(164, 22); + this.itemgrid64.Tag = "64"; + this.itemgrid64.Text = "64 mp"; + this.itemgrid64.Click += new System.EventHandler(this.itemgridsize_Click); + // + // itemgrid32 + // + this.itemgrid32.Name = "itemgrid32"; + this.itemgrid32.Size = new System.Drawing.Size(164, 22); + this.itemgrid32.Tag = "32"; + this.itemgrid32.Text = "32 mp"; + this.itemgrid32.Click += new System.EventHandler(this.itemgridsize_Click); + // + // itemgrid16 + // + this.itemgrid16.Name = "itemgrid16"; + this.itemgrid16.Size = new System.Drawing.Size(164, 22); + this.itemgrid16.Tag = "16"; + this.itemgrid16.Text = "16 mp"; + this.itemgrid16.Click += new System.EventHandler(this.itemgridsize_Click); + // + // itemgrid8 + // + this.itemgrid8.Name = "itemgrid8"; + this.itemgrid8.Size = new System.Drawing.Size(164, 22); + this.itemgrid8.Tag = "8"; + this.itemgrid8.Text = "8 mp"; + this.itemgrid8.Click += new System.EventHandler(this.itemgridsize_Click); + // + // itemgrid4 + // + this.itemgrid4.Name = "itemgrid4"; + this.itemgrid4.Size = new System.Drawing.Size(164, 22); + this.itemgrid4.Tag = "4"; + this.itemgrid4.Text = "4 mp"; + this.itemgrid4.Click += new System.EventHandler(this.itemgridsize_Click); + // + // itemgridcustom + // + this.itemgridcustom.Name = "itemgridcustom"; + this.itemgridcustom.Size = new System.Drawing.Size(164, 22); + this.itemgridcustom.Text = "Customize..."; + this.itemgridcustom.Click += new System.EventHandler(this.itemgridcustom_Click); + // + // zoomlabel + // + this.zoomlabel.AutoSize = false; + this.zoomlabel.AutoToolTip = true; + this.zoomlabel.Name = "zoomlabel"; + this.zoomlabel.Size = new System.Drawing.Size(54, 18); + this.zoomlabel.Text = "50%"; + this.zoomlabel.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + this.zoomlabel.TextImageRelation = System.Windows.Forms.TextImageRelation.Overlay; + this.zoomlabel.ToolTipText = "Zoom level"; + // + // buttonzoom + // + this.buttonzoom.AutoToolTip = false; + this.buttonzoom.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this.buttonzoom.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.itemzoom200, + this.itemzoom100, + this.itemzoom50, + this.itemzoom25, + this.itemzoom10, + this.itemzoom5, + toolStripSeparator2, + this.itemzoomfittoscreen}); + this.buttonzoom.Image = global::CodeImp.DoomBuilder.Properties.Resources.Zoom_arrowup; + this.buttonzoom.ImageScaling = System.Windows.Forms.ToolStripItemImageScaling.None; + this.buttonzoom.ImageTransparentColor = System.Drawing.Color.Transparent; + this.buttonzoom.Name = "buttonzoom"; + this.buttonzoom.ShowDropDownArrow = false; + this.buttonzoom.Size = new System.Drawing.Size(29, 21); + this.buttonzoom.Text = "Zoom"; + // + // itemzoom200 + // + this.itemzoom200.Name = "itemzoom200"; + this.itemzoom200.Size = new System.Drawing.Size(167, 22); + this.itemzoom200.Tag = "200"; + this.itemzoom200.Text = "200%"; + this.itemzoom200.Click += new System.EventHandler(this.itemzoomto_Click); + // + // itemzoom100 + // + this.itemzoom100.Name = "itemzoom100"; + this.itemzoom100.Size = new System.Drawing.Size(167, 22); + this.itemzoom100.Tag = "100"; + this.itemzoom100.Text = "100%"; + this.itemzoom100.Click += new System.EventHandler(this.itemzoomto_Click); + // + // itemzoom50 + // + this.itemzoom50.Name = "itemzoom50"; + this.itemzoom50.Size = new System.Drawing.Size(167, 22); + this.itemzoom50.Tag = "50"; + this.itemzoom50.Text = "50%"; + this.itemzoom50.Click += new System.EventHandler(this.itemzoomto_Click); + // + // itemzoom25 + // + this.itemzoom25.Name = "itemzoom25"; + this.itemzoom25.Size = new System.Drawing.Size(167, 22); + this.itemzoom25.Tag = "25"; + this.itemzoom25.Text = "25%"; + this.itemzoom25.Click += new System.EventHandler(this.itemzoomto_Click); + // + // itemzoom10 + // + this.itemzoom10.Name = "itemzoom10"; + this.itemzoom10.Size = new System.Drawing.Size(167, 22); + this.itemzoom10.Tag = "10"; + this.itemzoom10.Text = "10%"; + this.itemzoom10.Click += new System.EventHandler(this.itemzoomto_Click); + // + // itemzoom5 + // + this.itemzoom5.Name = "itemzoom5"; + this.itemzoom5.Size = new System.Drawing.Size(167, 22); + this.itemzoom5.Tag = "5"; + this.itemzoom5.Text = "5%"; + this.itemzoom5.Click += new System.EventHandler(this.itemzoomto_Click); + // + // itemzoomfittoscreen + // + this.itemzoomfittoscreen.Name = "itemzoomfittoscreen"; + this.itemzoomfittoscreen.Size = new System.Drawing.Size(167, 22); + this.itemzoomfittoscreen.Text = "Fit to screen"; + this.itemzoomfittoscreen.Click += new System.EventHandler(this.itemzoomfittoscreen_Click); + // + // xposlabel + // + this.xposlabel.AutoSize = false; + this.xposlabel.Name = "xposlabel"; + this.xposlabel.Size = new System.Drawing.Size(50, 18); + this.xposlabel.Text = "0"; + this.xposlabel.ToolTipText = "Current X, Y coordinates on map"; + // + // yposlabel + // + this.yposlabel.AutoSize = false; + this.yposlabel.Name = "yposlabel"; + this.yposlabel.Size = new System.Drawing.Size(50, 18); + this.yposlabel.Text = "0"; + this.yposlabel.ToolTipText = "Current X, Y coordinates on map"; + // + // panelinfo + // + this.panelinfo.Controls.Add(this.heightpanel1); + this.panelinfo.Controls.Add(this.vertexinfo); + this.panelinfo.Controls.Add(this.labelcollapsedinfo); + this.panelinfo.Controls.Add(this.buttontoggleinfo); + this.panelinfo.Controls.Add(this.modename); + this.panelinfo.Controls.Add(this.linedefinfo); + this.panelinfo.Controls.Add(this.thinginfo); + this.panelinfo.Controls.Add(this.sectorinfo); + this.panelinfo.Dock = System.Windows.Forms.DockStyle.Bottom; + this.panelinfo.Location = new System.Drawing.Point(26, 564); + this.panelinfo.Name = "panelinfo"; + this.panelinfo.Size = new System.Drawing.Size(986, 106); + this.panelinfo.TabIndex = 4; + // + // heightpanel1 + // + this.heightpanel1.BackColor = System.Drawing.Color.Navy; + this.heightpanel1.ForeColor = System.Drawing.SystemColors.ControlText; + this.heightpanel1.Location = new System.Drawing.Point(0, 0); + this.heightpanel1.Name = "heightpanel1"; + this.heightpanel1.Size = new System.Drawing.Size(29, 106); + this.heightpanel1.TabIndex = 7; + this.heightpanel1.Visible = false; + // + // vertexinfo + // + this.vertexinfo.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.vertexinfo.Location = new System.Drawing.Point(3, 3); + this.vertexinfo.MaximumSize = new System.Drawing.Size(10000, 100); + this.vertexinfo.MinimumSize = new System.Drawing.Size(100, 100); + this.vertexinfo.Name = "vertexinfo"; + this.vertexinfo.Size = new System.Drawing.Size(310, 100); + this.vertexinfo.TabIndex = 1; + this.vertexinfo.Visible = false; + // + // labelcollapsedinfo + // + this.labelcollapsedinfo.AutoSize = true; + this.labelcollapsedinfo.Font = new System.Drawing.Font("Verdana", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.labelcollapsedinfo.Location = new System.Drawing.Point(2, 2); + this.labelcollapsedinfo.Name = "labelcollapsedinfo"; + this.labelcollapsedinfo.Size = new System.Drawing.Size(137, 13); + this.labelcollapsedinfo.TabIndex = 6; + this.labelcollapsedinfo.Text = "Collapsed Descriptions"; + this.labelcollapsedinfo.Visible = false; + // + // buttontoggleinfo + // + this.buttontoggleinfo.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.buttontoggleinfo.FlatStyle = System.Windows.Forms.FlatStyle.Popup; + this.buttontoggleinfo.Font = new System.Drawing.Font("Marlett", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(2))); + this.buttontoggleinfo.Location = new System.Drawing.Point(962, 1); + this.buttontoggleinfo.Name = "buttontoggleinfo"; + this.buttontoggleinfo.Size = new System.Drawing.Size(22, 19); + this.buttontoggleinfo.TabIndex = 5; + this.buttontoggleinfo.TabStop = false; + this.buttontoggleinfo.Tag = "builder_toggleinfopanel"; + this.buttontoggleinfo.Text = "6"; + this.buttontoggleinfo.UseVisualStyleBackColor = true; + this.buttontoggleinfo.Click += new System.EventHandler(this.InvokeTaggedAction); + this.buttontoggleinfo.MouseUp += new System.Windows.Forms.MouseEventHandler(this.buttontoggleinfo_MouseUp); + // + // modename + // + this.modename.AutoSize = true; + this.modename.Font = new System.Drawing.Font("Verdana", 36F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.modename.ForeColor = System.Drawing.SystemColors.GrayText; + this.modename.Location = new System.Drawing.Point(12, 20); + this.modename.Name = "modename"; + this.modename.Size = new System.Drawing.Size(244, 59); + this.modename.TabIndex = 4; + this.modename.Text = "Vertices"; + this.modename.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; + this.modename.UseMnemonic = false; + this.modename.Visible = false; + // + // linedefinfo + // + this.linedefinfo.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.linedefinfo.Location = new System.Drawing.Point(3, 3); + this.linedefinfo.MaximumSize = new System.Drawing.Size(10000, 100); + this.linedefinfo.MinimumSize = new System.Drawing.Size(100, 100); + this.linedefinfo.Name = "linedefinfo"; + this.linedefinfo.Size = new System.Drawing.Size(1039, 100); + this.linedefinfo.TabIndex = 0; + this.linedefinfo.Visible = false; + // + // thinginfo + // + this.thinginfo.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.thinginfo.Location = new System.Drawing.Point(3, 3); + this.thinginfo.MaximumSize = new System.Drawing.Size(10000, 100); + this.thinginfo.MinimumSize = new System.Drawing.Size(100, 100); + this.thinginfo.Name = "thinginfo"; + this.thinginfo.Size = new System.Drawing.Size(639, 100); + this.thinginfo.TabIndex = 3; + this.thinginfo.Visible = false; + // + // sectorinfo + // + this.sectorinfo.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.sectorinfo.Location = new System.Drawing.Point(3, 3); + this.sectorinfo.MaximumSize = new System.Drawing.Size(10000, 100); + this.sectorinfo.MinimumSize = new System.Drawing.Size(100, 100); + this.sectorinfo.Name = "sectorinfo"; + this.sectorinfo.Size = new System.Drawing.Size(768, 100); + this.sectorinfo.TabIndex = 2; + this.sectorinfo.Visible = false; + // + // redrawtimer + // + this.redrawtimer.Interval = 1; + this.redrawtimer.Tick += new System.EventHandler(this.redrawtimer_Tick); + // + // processor + // + this.processor.Interval = 10; + this.processor.Tick += new System.EventHandler(this.processor_Tick); + // + // statusflasher + // + this.statusflasher.Tick += new System.EventHandler(this.statusflasher_Tick); + // + // statusresetter + // + this.statusresetter.Tick += new System.EventHandler(this.statusresetter_Tick); + // + // dockersspace + // + this.dockersspace.Dock = System.Windows.Forms.DockStyle.Left; + this.dockersspace.Location = new System.Drawing.Point(0, 49); + this.dockersspace.Name = "dockersspace"; + this.dockersspace.Size = new System.Drawing.Size(26, 621); + this.dockersspace.TabIndex = 6; + // + // dockerspanel + // + this.dockerspanel.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.dockerspanel.Location = new System.Drawing.Point(62, 67); + this.dockerspanel.Name = "dockerspanel"; + this.dockerspanel.Size = new System.Drawing.Size(236, 467); + this.dockerspanel.TabIndex = 7; + this.dockerspanel.TabStop = false; + this.dockerspanel.UserResize += new System.EventHandler(this.dockerspanel_UserResize); + this.dockerspanel.Collapsed += new System.EventHandler(this.LoseFocus); + this.dockerspanel.MouseContainerEnter += new System.EventHandler(this.dockerspanel_MouseContainerEnter); + // + // dockerscollapser + // + this.dockerscollapser.Interval = 200; + this.dockerscollapser.Tick += new System.EventHandler(this.dockerscollapser_Tick); + // + // MainForm + // + this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; + this.ClientSize = new System.Drawing.Size(1012, 693); + this.Controls.Add(this.dockerspanel); + this.Controls.Add(this.display); + this.Controls.Add(this.panelinfo); + this.Controls.Add(this.dockersspace); + this.Controls.Add(this.statusbar); + this.Controls.Add(this.toolbar); + this.Controls.Add(this.menumain); + this.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); + this.KeyPreview = true; + this.MainMenuStrip = this.menumain; + this.Name = "MainForm"; + this.StartPosition = System.Windows.Forms.FormStartPosition.Manual; + this.Text = "Doom Builder"; + this.Deactivate += new System.EventHandler(this.MainForm_Deactivate); + this.Load += new System.EventHandler(this.MainForm_Load); + this.Shown += new System.EventHandler(this.MainForm_Shown); + this.Activated += new System.EventHandler(this.MainForm_Activated); + this.KeyUp += new System.Windows.Forms.KeyEventHandler(this.MainForm_KeyUp); + this.Move += new System.EventHandler(this.MainForm_Move); + this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.MainForm_FormClosing); + this.Resize += new System.EventHandler(this.MainForm_Resize); + this.KeyDown += new System.Windows.Forms.KeyEventHandler(this.MainForm_KeyDown); + this.ResizeEnd += new System.EventHandler(this.MainForm_ResizeEnd); + this.menumain.ResumeLayout(false); + this.menumain.PerformLayout(); + this.toolbar.ResumeLayout(false); + this.toolbar.PerformLayout(); + this.statusbar.ResumeLayout(false); + this.statusbar.PerformLayout(); + this.panelinfo.ResumeLayout(false); + this.panelinfo.PerformLayout(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.MenuStrip menumain; + private System.Windows.Forms.ToolStrip toolbar; + private System.Windows.Forms.StatusStrip statusbar; + private System.Windows.Forms.Panel panelinfo; + private System.Windows.Forms.ToolStripMenuItem menufile; + private System.Windows.Forms.ToolStripMenuItem itemnewmap; + private System.Windows.Forms.ToolStripMenuItem itemopenmap; + private System.Windows.Forms.ToolStripMenuItem itemsavemap; + private System.Windows.Forms.ToolStripMenuItem itemsavemapas; + private System.Windows.Forms.ToolStripMenuItem itemsavemapinto; + private System.Windows.Forms.ToolStripMenuItem itemexit; + private System.Windows.Forms.ToolStripStatusLabel statuslabel; + private System.Windows.Forms.ToolStripMenuItem itemclosemap; + private System.Windows.Forms.Timer redrawtimer; + private System.Windows.Forms.ToolStripMenuItem menuhelp; + private System.Windows.Forms.ToolStripMenuItem itemhelpabout; + private CodeImp.DoomBuilder.Controls.RenderTargetControl display; + private System.Windows.Forms.ToolStripMenuItem itemnorecent; + private System.Windows.Forms.ToolStripStatusLabel xposlabel; + private System.Windows.Forms.ToolStripStatusLabel yposlabel; + private System.Windows.Forms.ToolStripButton buttonnewmap; + private System.Windows.Forms.ToolStripButton buttonopenmap; + private System.Windows.Forms.ToolStripButton buttonsavemap; + private System.Windows.Forms.ToolStripStatusLabel zoomlabel; + private System.Windows.Forms.ToolStripDropDownButton buttonzoom; + private System.Windows.Forms.ToolStripMenuItem itemzoomfittoscreen; + private System.Windows.Forms.ToolStripMenuItem itemzoom100; + private System.Windows.Forms.ToolStripMenuItem itemzoom200; + private System.Windows.Forms.ToolStripMenuItem itemzoom50; + private System.Windows.Forms.ToolStripMenuItem itemzoom25; + private System.Windows.Forms.ToolStripMenuItem itemzoom10; + private System.Windows.Forms.ToolStripMenuItem itemzoom5; + private System.Windows.Forms.ToolStripMenuItem menutools; + private System.Windows.Forms.ToolStripMenuItem configurationToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem preferencesToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem menuedit; + private System.Windows.Forms.ToolStripMenuItem itemmapoptions; + private System.Windows.Forms.ToolStripButton buttonmapoptions; + private System.Windows.Forms.ToolStripMenuItem itemreloadresources; + private CodeImp.DoomBuilder.Controls.LinedefInfoPanel linedefinfo; + private CodeImp.DoomBuilder.Controls.VertexInfoPanel vertexinfo; + private CodeImp.DoomBuilder.Controls.SectorInfoPanel sectorinfo; + private CodeImp.DoomBuilder.Controls.ThingInfoPanel thinginfo; + private System.Windows.Forms.ToolStripButton buttonthingsfilter; + private System.Windows.Forms.ToolStripComboBox thingfilters; + private System.Windows.Forms.ToolStripSeparator toolStripSeparator8; + private System.Windows.Forms.ToolStripStatusLabel gridlabel; + private System.Windows.Forms.ToolStripDropDownButton buttongrid; + private System.Windows.Forms.ToolStripMenuItem itemgrid1024; + private System.Windows.Forms.ToolStripMenuItem itemgrid256; + private System.Windows.Forms.ToolStripMenuItem itemgrid128; + private System.Windows.Forms.ToolStripMenuItem itemgrid64; + private System.Windows.Forms.ToolStripMenuItem itemgrid32; + private System.Windows.Forms.ToolStripMenuItem itemgrid16; + private System.Windows.Forms.ToolStripMenuItem itemgrid4; + private System.Windows.Forms.ToolStripMenuItem itemgrid8; + private System.Windows.Forms.ToolStripMenuItem itemgridcustom; + private System.Windows.Forms.ToolStripMenuItem itemgrid512; + private System.Windows.Forms.ToolStripStatusLabel poscommalabel; + private System.Windows.Forms.ToolStripMenuItem itemundo; + private System.Windows.Forms.ToolStripMenuItem itemredo; + private System.Windows.Forms.ToolStripButton buttonundo; + private System.Windows.Forms.ToolStripButton buttonredo; + private System.Windows.Forms.ToolStripButton buttonsnaptogrid; + private System.Windows.Forms.ToolStripMenuItem itemsnaptogrid; + private System.Windows.Forms.ToolStripButton buttonautomerge; + private System.Windows.Forms.ToolStripMenuItem itemautomerge; + private System.Windows.Forms.ToolStripButton buttonfullbrightness; + private System.Windows.Forms.ToolStripMenuItem itemfullbrightness; + private System.Windows.Forms.ToolStripSeparator buttoneditmodesseperator; + private System.Windows.Forms.Timer processor; + private System.Windows.Forms.ToolStripSeparator toolStripSeparator5; + private System.Windows.Forms.ToolStripSeparator toolStripMenuItem5; + private System.Windows.Forms.ToolStripSeparator toolStripSeparator6; + private System.Windows.Forms.ToolStripSeparator toolStripMenuItem6; + private System.Windows.Forms.ToolStripMenuItem itemgridinc; + private System.Windows.Forms.ToolStripMenuItem itemgriddec; + private System.Windows.Forms.ToolStripMenuItem itemgridsetup; + private System.Windows.Forms.Label modename; + private System.Windows.Forms.Timer statusflasher; + private System.Windows.Forms.ToolStripSplitButton buttontest; + private System.Windows.Forms.ToolStripButton buttoncut; + private System.Windows.Forms.ToolStripButton buttoncopy; + private System.Windows.Forms.ToolStripButton buttonpaste; + private System.Windows.Forms.ToolStripSeparator toolStripMenuItem7; + private System.Windows.Forms.ToolStripMenuItem itemcut; + private System.Windows.Forms.ToolStripMenuItem itemcopy; + private System.Windows.Forms.ToolStripMenuItem itempaste; + private System.Windows.Forms.ToolStripStatusLabel configlabel; + private System.Windows.Forms.ToolStripMenuItem menumode; + private System.Windows.Forms.ToolStripButton buttonviewnormal; + private System.Windows.Forms.ToolStripButton buttonviewfloors; + private System.Windows.Forms.ToolStripButton buttonviewceilings; + private System.Windows.Forms.ToolStripSeparator toolStripMenuItem8; + private System.Windows.Forms.ToolStripButton buttonscripteditor; + private System.Windows.Forms.ToolStripMenuItem menuview; + private System.Windows.Forms.ToolStripMenuItem itemthingsfilter; + private System.Windows.Forms.ToolStripSeparator toolStripMenuItem9; + private System.Windows.Forms.ToolStripMenuItem itemviewnormal; + private System.Windows.Forms.ToolStripMenuItem itemviewfloors; + private System.Windows.Forms.ToolStripMenuItem itemviewceilings; + private System.Windows.Forms.ToolStripSeparator toolStripMenuItem10; + private System.Windows.Forms.ToolStripMenuItem itemscripteditor; + private System.Windows.Forms.ToolStripSeparator toolStripMenuItem11; + private System.Windows.Forms.ToolStripMenuItem itemtestmap; + private System.Windows.Forms.ToolStripMenuItem menuprefabs; + private System.Windows.Forms.ToolStripMenuItem itemcreateprefab; + private System.Windows.Forms.ToolStripSeparator toolStripMenuItem12; + private System.Windows.Forms.ToolStripMenuItem iteminsertprefabfile; + private System.Windows.Forms.ToolStripMenuItem iteminsertpreviousprefab; + private System.Windows.Forms.ToolStripButton buttoninsertprefabfile; + private System.Windows.Forms.ToolStripButton buttoninsertpreviousprefab; + private System.Windows.Forms.Button buttontoggleinfo; + private System.Windows.Forms.Label labelcollapsedinfo; + private System.Windows.Forms.Timer statusresetter; + private System.Windows.Forms.ToolStripMenuItem itemshowerrors; + private System.Windows.Forms.ToolStripSeparator toolStripMenuItem2; + private System.Windows.Forms.ToolStripMenuItem menuzoom; + private System.Windows.Forms.ToolStripMenuItem item2zoom5; + private System.Windows.Forms.ToolStripMenuItem item2zoom10; + private System.Windows.Forms.ToolStripMenuItem itemfittoscreen; + private System.Windows.Forms.ToolStripMenuItem item2zoom200; + private System.Windows.Forms.ToolStripMenuItem item2zoom100; + private System.Windows.Forms.ToolStripMenuItem item2zoom50; + private System.Windows.Forms.ToolStripMenuItem item2zoom25; + private System.Windows.Forms.ToolStripMenuItem itemhelprefmanual; + private System.Windows.Forms.ToolStripSeparator toolStripMenuItem13; + private System.Windows.Forms.ToolStripMenuItem itemhelpeditmode; + private System.Windows.Forms.ToolStripMenuItem itemtoggleinfo; + private System.Windows.Forms.ToolStripMenuItem itempastespecial; + private System.Windows.Forms.Panel heightpanel1; + private System.Windows.Forms.Panel dockersspace; + private CodeImp.DoomBuilder.Controls.DockersControl dockerspanel; + private System.Windows.Forms.Timer dockerscollapser; + private System.Windows.Forms.ToolStripButton buttonviewfloorcolor; + private System.Windows.Forms.ToolStripMenuItem itemviewfloorcolor; + private System.Windows.Forms.ToolStripButton buttonviewceilingcolor; + private System.Windows.Forms.ToolStripMenuItem itemviewceilingcolor; + private System.Windows.Forms.ToolStripButton buttonviewthingcolor; + private System.Windows.Forms.ToolStripMenuItem itemviewthingcolor; + } +} \ No newline at end of file diff --git a/Source/Core/Windows/MainForm.cs b/Source/Core/Windows/MainForm.cs new file mode 100644 index 0000000..3486ace --- /dev/null +++ b/Source/Core/Windows/MainForm.cs @@ -0,0 +1,2673 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Text; +using System.Globalization; +using System.Windows.Forms; +using CodeImp.DoomBuilder.Actions; +using CodeImp.DoomBuilder.Geometry; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Editing; +using System.Collections; +using System.IO; +using CodeImp.DoomBuilder.Map; +using System.Reflection; +using CodeImp.DoomBuilder.Plugins; +using CodeImp.DoomBuilder.Controls; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Properties; +using CodeImp.DoomBuilder.Config; +using CodeImp.DoomBuilder.Data; +using System.Threading; +using System.Runtime.InteropServices; + +#endregion + +namespace CodeImp.DoomBuilder.Windows +{ + public partial class MainForm : DelayedForm, IMainForm + { + #region ================== Constants + + // Recent files + private const int MAX_RECENT_FILES = 8; + private const int MAX_RECENT_FILES_PIXELS = 250; + + // Dockers + private const int DOCKER_TAB_WIDTH = 20; + + // Status bar + private const string STATUS_READY_TEXT = "Ready."; + private const string STATUS_LOADING_TEXT = "Loading resources..."; + private const int WARNING_FLASH_COUNT = 10; + private const int WARNING_FLASH_INTERVAL = 100; + private const int WARNING_RESET_DELAY = 4000; + private const int INFO_RESET_DELAY = 4000; + private const int ACTION_FLASH_COUNT = 3; + private const int ACTION_FLASH_INTERVAL = 50; + private const int ACTION_RESET_DELAY = 4000; + + private readonly Image[,] STATUS_IMAGES = new Image[2, 4] + { + // Normal versions + { + Properties.Resources.Status0, Properties.Resources.Status1, + Properties.Resources.Status2, Properties.Resources.Warning + }, + + // Flashing versions + { + Properties.Resources.Status10, Properties.Resources.Status11, + Properties.Resources.Status12, Properties.Resources.WarningOff + } + }; + + // Message pump + public enum ThreadMessages : int + { + // Sent by the background threat to update the status + UpdateStatus = General.WM_USER + 1, + + // This is sent by the background thread when images are loaded + // but only when first loaded or when dimensions were changed + ImageDataLoaded = General.WM_USER + 2 + } + + #endregion + + #region ================== Delegates + + private delegate void CallUpdateStatusIcon(); + private delegate void CallImageDataLoaded(ImageData img); + + #endregion + + #region ================== Variables + + // Position/size + private Point lastposition; + private Size lastsize; + private bool displayresized = true; + private bool windowactive; + + // Mouse in display + private bool mouseinside; + + // Input + private bool shift, ctrl, alt; + private MouseButtons mousebuttons; + private MouseInput mouseinput; + private Rectangle originalclip; + private bool mouseexclusive; + private int mouseexclusivebreaklevel; + + // Skills + private ToolStripItem[] skills; + + // Last info on panels + private object lastinfoobject; + + // Recent files + private ToolStripMenuItem[] recentitems; + + // View modes + private ToolStripButton[] viewmodesbuttons; + private ToolStripMenuItem[] viewmodesitems; + + // Edit modes + private List editmodeitems; + + // Toolbar + private EventHandler buttonvisiblechangedhandler; + private bool updatingfilters; + + // Statusbar + private StatusInfo status; + private int statusflashcount; + private bool statusflashicon; + + // Properties + private IntPtr windowptr; + + // Processing + private int processingcount; + private double lastupdatetime; + + // Updating + private int lockupdatecount; + private bool mapchanged; + + #endregion + + #region ================== Properties + + public bool ShiftState { get { return shift; } } + public bool CtrlState { get { return ctrl; } } + public bool AltState { get { return alt; } } + public MouseButtons MouseButtons { get { return mousebuttons; } } + public bool MouseInDisplay { get { return mouseinside; } } + public RenderTargetControl Display { get { return display; } } + public bool SnapToGrid { get { return buttonsnaptogrid.Checked; } } + public bool AutoMerge { get { return buttonautomerge.Checked; } } + public bool MouseExclusive { get { return mouseexclusive; } } + new public IntPtr Handle { get { return windowptr; } } + public bool IsInfoPanelExpanded { get { return (panelinfo.Height == heightpanel1.Height); } } + public bool IsActiveWindow { get { return windowactive; } } + public StatusInfo Status { get { return status; } } + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + internal MainForm() + { + // Setup controls + InitializeComponent(); + editmodeitems = new List(); + labelcollapsedinfo.Text = ""; + display.Dock = DockStyle.Fill; + + // Fetch pointer + windowptr = base.Handle; + + // Make array for view modes + viewmodesbuttons = new ToolStripButton[Renderer2D.NUM_VIEW_MODES]; + viewmodesbuttons[(int)ViewMode.Normal] = buttonviewnormal; + viewmodesbuttons[(int)ViewMode.FloorTextures] = buttonviewfloors; + viewmodesbuttons[(int)ViewMode.CeilingTextures] = buttonviewceilings; + viewmodesbuttons[(int)ViewMode.FloorColor] = buttonviewfloorcolor; // villsa + viewmodesbuttons[(int)ViewMode.CeilingColor] = buttonviewceilingcolor; // villsa + viewmodesbuttons[(int)ViewMode.ThingColor] = buttonviewthingcolor; // villsa + viewmodesitems = new ToolStripMenuItem[Renderer2D.NUM_VIEW_MODES]; + viewmodesitems[(int)ViewMode.Normal] = itemviewnormal; + viewmodesitems[(int)ViewMode.FloorTextures] = itemviewfloors; + viewmodesitems[(int)ViewMode.CeilingTextures] = itemviewceilings; + viewmodesitems[(int)ViewMode.FloorColor] = itemviewfloorcolor; // villsa + viewmodesitems[(int)ViewMode.CeilingColor] = itemviewceilingcolor; // villsa + viewmodesitems[(int)ViewMode.ThingColor] = itemviewthingcolor; // villsa + + // Visual Studio IDE doesn't let me set these in the designer :( + buttonzoom.Font = menufile.Font; + buttonzoom.DropDownDirection = ToolStripDropDownDirection.AboveLeft; + buttongrid.Font = menufile.Font; + buttongrid.DropDownDirection = ToolStripDropDownDirection.AboveLeft; + + // Event handlers + buttonvisiblechangedhandler = new EventHandler(ToolbarButtonVisibleChanged); + + // Bind any methods + General.Actions.BindMethods(this); + + // Apply shortcut keys + ApplyShortcutKeys(); + + // Make recent items list + CreateRecentFiles(); + + // Show splash + ShowSplashDisplay(); + + // Keep last position and size + lastposition = this.Location; + lastsize = this.Size; + } + + #endregion + + #region ================== General + + // Editing mode changed! + internal void EditModeChanged() + { + // Check appropriate button on interface + // And show the mode name + if (General.Editing.Mode != null) + { + General.MainWindow.CheckEditModeButton(General.Editing.Mode.EditModeButtonName); + General.MainWindow.DisplayModeName(General.Editing.Mode.Attributes.DisplayName); + } + else + { + General.MainWindow.CheckEditModeButton(""); + General.MainWindow.DisplayModeName(""); + } + + // View mode only matters in classic editing modes + for (int i = 0; i < Renderer2D.NUM_VIEW_MODES; i++) + { + viewmodesitems[i].Enabled = (General.Editing.Mode is ClassicMode); + viewmodesbuttons[i].Enabled = (General.Editing.Mode is ClassicMode); + } + } + + // This makes a beep sound + public void MessageBeep(MessageBeepType type) + { + General.MessageBeep(type); + } + + // This sets up the interface + internal void SetupInterface() + { + float scalex = this.CurrentAutoScaleDimensions.Width / this.AutoScaleDimensions.Width; + float scaley = this.CurrentAutoScaleDimensions.Height / this.AutoScaleDimensions.Height; + + // Setup docker + if (General.Settings.DockersPosition != 2) + { + LockUpdate(); + dockerspanel.Visible = true; + dockersspace.Visible = true; + + // We can't place the docker easily when collapsed + dockerspanel.Expand(); + + // Setup docker width + if (General.Settings.DockersWidth < dockerspanel.GetCollapsedWidth()) + General.Settings.DockersWidth = dockerspanel.GetCollapsedWidth(); + + // Determine fixed space required + if (General.Settings.CollapseDockers) + dockersspace.Width = dockerspanel.GetCollapsedWidth(); + else + dockersspace.Width = General.Settings.DockersWidth; + + // Setup docker + if (General.Settings.DockersPosition == 0) + { + dockersspace.Dock = DockStyle.Left; + dockerspanel.Setup(false); + dockerspanel.Location = dockersspace.Location; + dockerspanel.Anchor = AnchorStyles.Left | AnchorStyles.Top | AnchorStyles.Bottom; + } + else + { + dockersspace.Dock = DockStyle.Right; + dockerspanel.Setup(true); + dockerspanel.Location = new Point(dockersspace.Right - General.Settings.DockersWidth, dockersspace.Top); + dockerspanel.Anchor = AnchorStyles.Right | AnchorStyles.Top | AnchorStyles.Bottom; + } + + dockerspanel.Width = General.Settings.DockersWidth; + dockerspanel.Height = dockersspace.Height; + dockerspanel.BringToFront(); + + if (General.Settings.CollapseDockers) + dockerspanel.Collapse(); + + UnlockUpdate(); + } + else + { + dockerspanel.Visible = false; + dockersspace.Visible = false; + } + } + + // This updates all menus for the current status + internal void UpdateInterface() + { + // Update Doom Builder window name + UpdateTitle(); + + // Update the status bar + UpdateStatusbar(); + + // Update menus and toolbar icons + UpdateFileMenu(); + UpdateEditMenu(); + UpdateViewMenu(); + UpdateModeMenu(); + UpdatePrefabsMenu(); + UpdateToolsMenu(); + UpdateToolbar(); + UpdateSkills(); + UpdateHelpMenu(); + } + + private void UpdateTitle() + { + // Map opened? + if (General.Map != null) + { + // Show map name and filename in caption + this.Text = (mapchanged ? "\u25CF " : "") + General.Map.FileTitle + " (" + General.Map.Options.CurrentName + ") - " + Application.ProductName; + } + else + { + // Show normal caption + this.Text = Application.ProductName; + } + } + + // Generic event that invokes the tagged action + public void InvokeTaggedAction(object sender, EventArgs e) + { + string asmname; + + this.Update(); + + if (sender is ToolStripItem) + General.Actions.InvokeAction((sender as ToolStripItem).Tag.ToString()); + else if (sender is Control) + General.Actions.InvokeAction((sender as Control).Tag.ToString()); + else + General.Fail("InvokeTaggedAction used on an unexpected control."); + + this.Update(); + } + + #endregion + + #region ================== Window + + // This locks the window for updating + internal void LockUpdate() + { + lockupdatecount++; + if (lockupdatecount == 1) General.LockWindowUpdate(this.Handle); + } + + // This unlocks for updating + internal void UnlockUpdate() + { + lockupdatecount--; + if (lockupdatecount == 0) General.LockWindowUpdate(IntPtr.Zero); + if (lockupdatecount < 0) lockupdatecount = 0; + } + + internal void UpdateMapChangedStatus() + { + if (General.Map == null || General.Map.IsChanged == mapchanged) return; + mapchanged = General.Map.IsChanged; + UpdateTitle(); + } + + // This unlocks for updating + internal void ForceUnlockUpdate() + { + if (lockupdatecount > 0) General.LockWindowUpdate(IntPtr.Zero); + lockupdatecount = 0; + } + + // This sets the focus on the display for correct key input + public bool FocusDisplay() + { + return display.Focus(); + } + + // Window is first shown + private void MainForm_Shown(object sender, EventArgs e) + { + // Perform auto mapo loading action when the window is not delayed + if (!General.DelayMainWindow) PerformAutoMapLoading(); + } + + // Auto map loading that must be done when the window is first shown after loading + // but also before the window is shown when the -delaywindow parameter is given + internal void PerformAutoMapLoading() + { + // Check if the command line arguments tell us to load something + if (General.AutoLoadFile != null) + { + bool showdialog = false; + MapOptions options = new MapOptions(); + Configuration mapsettings; + + // Any of the options already given? + if (General.AutoLoadMap != null) + { + // Try to find existing options in the settings file + string dbsfile = General.AutoLoadFile.Substring(0, General.AutoLoadFile.Length - 4) + ".dbs"; + if (File.Exists(dbsfile)) + try { mapsettings = new Configuration(dbsfile, true); } + catch (Exception) { mapsettings = new Configuration(true); } + else + mapsettings = new Configuration(true); + + // Set map name and other options + options = new MapOptions(mapsettings, General.AutoLoadMap); + + // Set configuration file (constructor already does this, but we want this info from the cmd args if possible) + options.ConfigFile = General.AutoLoadConfig; + if (options.ConfigFile == null) options.ConfigFile = mapsettings.ReadSetting("gameconfig", ""); + if (options.ConfigFile.Trim().Length == 0) showdialog = true; + } + else + { + // No options given + showdialog = true; + } + + // Show open map dialog? + if (showdialog) + { + // Show open dialog + General.OpenMapFile(General.AutoLoadFile); + } + else + { + // Open with options + General.OpenMapFileWithOptions(General.AutoLoadFile, options); + } + } + } + + // Window is loaded + private void MainForm_Load(object sender, EventArgs e) + { + // Position window from configuration settings + this.SuspendLayout(); + this.Location = new Point(General.Settings.ReadSetting("mainwindow.positionx", this.Location.X), + General.Settings.ReadSetting("mainwindow.positiony", this.Location.Y)); + this.Size = new Size(General.Settings.ReadSetting("mainwindow.sizewidth", this.Size.Width), + General.Settings.ReadSetting("mainwindow.sizeheight", this.Size.Height)); + this.WindowState = (FormWindowState)General.Settings.ReadSetting("mainwindow.windowstate", (int)FormWindowState.Maximized); + this.ResumeLayout(true); + + // Normal windowstate? + if (this.WindowState == FormWindowState.Normal) + { + // Keep last position and size + lastposition = this.Location; + lastsize = this.Size; + } + + // Info panel state? + bool expandedpanel = General.Settings.ReadSetting("mainwindow.expandedinfopanel", true); + if (expandedpanel != IsInfoPanelExpanded) ToggleInfoPanel(); + } + + // Window receives focus + private void MainForm_Activated(object sender, EventArgs e) + { + windowactive = true; + + ResumeExclusiveMouseInput(); + ReleaseAllKeys(); + FocusDisplay(); + } + + // Window loses focus + private void MainForm_Deactivate(object sender, EventArgs e) + { + windowactive = false; + + BreakExclusiveMouseInput(); + ReleaseAllKeys(); + } + + // Window is moved + private void MainForm_Move(object sender, EventArgs e) + { + // Normal windowstate? + if (this.WindowState == FormWindowState.Normal) + { + // Keep last position and size + lastposition = this.Location; + lastsize = this.Size; + } + } + + // Window resizes + private void MainForm_Resize(object sender, EventArgs e) + { + // Resizing + //this.SuspendLayout(); + //resized = true; + } + + // Window was resized + private void MainForm_ResizeEnd(object sender, EventArgs e) + { + // Normal windowstate? + if (this.WindowState == FormWindowState.Normal) + { + // Keep last position and size + lastposition = this.Location; + lastsize = this.Size; + } + } + + // Window is being closed + private void MainForm_FormClosing(object sender, FormClosingEventArgs e) + { + int windowstate; + + if (e.CloseReason != CloseReason.ApplicationExitCall) + { + // Close the map + if (General.CloseMap()) + { + General.WriteLogLine("Closing main interface window..."); + + // Stop timers + statusflasher.Stop(); + statusresetter.Stop(); + + // Stop exclusive mode, if any is active + StopExclusiveMouseInput(); + StopProcessing(); + + // Unbind methods + General.Actions.UnbindMethods(this); + + // Determine window state to save + if (this.WindowState != FormWindowState.Minimized) + windowstate = (int)this.WindowState; + else + windowstate = (int)FormWindowState.Normal; + + // Save window settings + General.Settings.WriteSetting("mainwindow.positionx", lastposition.X); + General.Settings.WriteSetting("mainwindow.positiony", lastposition.Y); + General.Settings.WriteSetting("mainwindow.sizewidth", lastsize.Width); + General.Settings.WriteSetting("mainwindow.sizeheight", lastsize.Height); + General.Settings.WriteSetting("mainwindow.windowstate", windowstate); + General.Settings.WriteSetting("mainwindow.expandedinfopanel", IsInfoPanelExpanded); + + // Save recent files + SaveRecentFiles(); + + // Terminate the program + General.Terminate(true); + } + else + { + // Cancel the close + e.Cancel = true; + } + } + } + + #endregion + + #region ================== Statusbar + + // This updates the status bar + private void UpdateStatusbar() + { + // Map open? + if (General.Map != null) + { + // Enable items + xposlabel.Enabled = true; + yposlabel.Enabled = true; + poscommalabel.Enabled = true; + zoomlabel.Enabled = true; + buttonzoom.Enabled = true; + gridlabel.Enabled = true; + buttongrid.Enabled = true; + configlabel.Text = General.Map.Config.Name; + } + else + { + // Disable items + xposlabel.Text = "--"; + yposlabel.Text = "--"; + xposlabel.Enabled = false; + yposlabel.Enabled = false; + poscommalabel.Enabled = false; + zoomlabel.Enabled = false; + buttonzoom.Enabled = false; + gridlabel.Enabled = false; + buttongrid.Enabled = false; + configlabel.Text = ""; + } + + UpdateStatusIcon(); + } + + // This flashes the status icon + private void statusflasher_Tick(object sender, EventArgs e) + { + statusflashicon = !statusflashicon; + UpdateStatusIcon(); + statusflashcount--; + if (statusflashcount == 0) statusflasher.Stop(); + } + + // This resets the status to ready + private void statusresetter_Tick(object sender, EventArgs e) + { + DisplayReady(); + } + + // This changes status text + public void DisplayStatus(StatusType type, string message) { DisplayStatus(new StatusInfo(type, message)); } + public void DisplayStatus(StatusInfo newstatus) + { + // Stop timers + if (!newstatus.displayed) + { + statusresetter.Stop(); + statusflasher.Stop(); + statusflashicon = false; + } + + // Determine what to do specifically for this status type + switch (newstatus.type) + { + // When no particular information is to be displayed. + // The messages displayed depends on running background processes. + case StatusType.Ready: + if ((General.Map != null) && (General.Map.Data != null) && General.Map.Data.IsLoading) + newstatus.message = STATUS_LOADING_TEXT; + else + newstatus.message = STATUS_READY_TEXT; + break; + + // Shows information without flashing the icon. + case StatusType.Info: + if (!newstatus.displayed) + { + statusresetter.Interval = INFO_RESET_DELAY; + statusresetter.Start(); + } + break; + + // Shows action information and flashes up the status icon once. + case StatusType.Action: + if (!newstatus.displayed) + { + statusflashicon = true; + statusflasher.Interval = ACTION_FLASH_INTERVAL; + statusflashcount = ACTION_FLASH_COUNT; + statusflasher.Start(); + statusresetter.Interval = ACTION_RESET_DELAY; + statusresetter.Start(); + } + break; + + // Shows a warning, makes a warning sound and flashes a warning icon. + case StatusType.Warning: + if (!newstatus.displayed) + { + MessageBeep(MessageBeepType.Warning); + statusflasher.Interval = WARNING_FLASH_INTERVAL; + statusflashcount = WARNING_FLASH_COUNT; + statusflasher.Start(); + statusresetter.Interval = WARNING_RESET_DELAY; + statusresetter.Start(); + } + break; + } + + // Update status description + status = newstatus; + status.displayed = true; + if (statuslabel.Text != status.message) + statuslabel.Text = status.message; + + // Update icon as well + UpdateStatusIcon(); + + // Refresh + statusbar.Invalidate(); + this.Update(); + } + + // This changes status text to Ready + public void DisplayReady() + { + DisplayStatus(StatusType.Ready, null); + } + + // This updates the status icon + internal void UpdateStatusIcon() + { + int statusicon = 0; + int statusflashindex = statusflashicon ? 1 : 0; + + // Loading icon? + if ((General.Map != null) && (General.Map.Data != null) && General.Map.Data.IsLoading) + statusicon = 1; + + // Status type + switch (status.type) + { + case StatusType.Ready: + case StatusType.Info: + case StatusType.Action: + statuslabel.Image = STATUS_IMAGES[statusflashindex, statusicon]; + break; + + case StatusType.Busy: + statuslabel.Image = STATUS_IMAGES[statusflashindex, 2]; + break; + + case StatusType.Warning: + statuslabel.Image = STATUS_IMAGES[statusflashindex, 3]; + break; + } + } + + // This changes coordinates display + public void UpdateCoordinates(Vector2D coords) + { + // X position + if (float.IsNaN(coords.x)) + xposlabel.Text = "--"; + else + xposlabel.Text = coords.x.ToString("####0"); + + // Y position + if (float.IsNaN(coords.y)) + yposlabel.Text = "--"; + else + yposlabel.Text = coords.y.ToString("####0"); + + // Update status bar + //statusbar.Update(); + } + + // This changes zoom display + internal void UpdateZoom(float scale) + { + // Update scale label + if (float.IsNaN(scale)) + zoomlabel.Text = "--"; + else + { + scale *= 100; + zoomlabel.Text = scale.ToString("##0") + "%"; + } + + // Update status bar + //statusbar.Update(); + } + + // Zoom to a specified level + private void itemzoomto_Click(object sender, EventArgs e) + { + int zoom; + + if (General.Map == null) return; + + // In classic mode? + if (General.Editing.Mode is ClassicMode) + { + // Requested from menu? + if (sender is ToolStripMenuItem) + { + // Get integral zoom level + zoom = int.Parse((sender as ToolStripMenuItem).Tag.ToString(), CultureInfo.InvariantCulture); + + // Zoom now + (General.Editing.Mode as ClassicMode).SetZoom((float)zoom / 100f); + } + } + } + + // Zoom to fit in screen + private void itemzoomfittoscreen_Click(object sender, EventArgs e) + { + if (General.Map == null) return; + + // In classic mode? + if (General.Editing.Mode is ClassicMode) + (General.Editing.Mode as ClassicMode).CenterInScreen(); + } + + // This changes grid display + internal void UpdateGrid(int gridsize) + { + // Update grid label + if (gridsize == 0) + gridlabel.Text = "--"; + else + gridlabel.Text = gridsize.ToString("###0") + " mp"; + + // Update status bar + //statusbar.Update(); + } + + // Set grid to a specified size + private void itemgridsize_Click(object sender, EventArgs e) + { + int size; + + if (General.Map == null) return; + + // In classic mode? + if (General.Editing.Mode is ClassicMode) + { + // Requested from menu? + if (sender is ToolStripMenuItem) + { + // Get integral zoom level + size = int.Parse((sender as ToolStripMenuItem).Tag.ToString(), CultureInfo.InvariantCulture); + + // Change grid size + General.Map.Grid.SetGridSize(size); + + // Redraw display + RedrawDisplay(); + } + } + } + + // Show grid setup + private void itemgridcustom_Click(object sender, EventArgs e) + { + if (General.Map == null) return; + + General.Map.Grid.ShowGridSetup(); + } + + #endregion + + #region ================== Display + + // This shows the splash screen on display + internal void ShowSplashDisplay() + { + // Change display to show splash logo + display.SetSplashLogoDisplay(); + display.Cursor = Cursors.Default; + this.Update(); + } + + // This clears the display + internal void ClearDisplay() + { + // Clear the display + display.SetManualRendering(); + this.Update(); + } + + // This sets the display cursor + public void SetCursor(Cursor cursor) + { + // Only when a map is open + if (General.Map != null) display.Cursor = cursor; + } + + // This redraws the display on the next paint event + public void RedrawDisplay() + { + if ((General.Map != null) && (General.Editing.Mode != null)) + { + foreach (Sector s in General.Map.Map.Sectors) + s.UpdateNeeded = true; + + General.Map.Map.Update(); + General.Editing.Mode.OnRedrawDisplay(); + } + else + { + display.Invalidate(); + } + } + + // This event is called when a repaint is needed + private void display_Paint(object sender, PaintEventArgs e) + { + if (General.Map != null) + { + if (General.Editing.Mode != null) + { + if (!displayresized) General.Editing.Mode.OnPresentDisplay(); + } + else + { + if (General.Colors != null) + e.Graphics.Clear(Color.FromArgb(General.Colors.Background.ToInt())); + else + e.Graphics.Clear(SystemColors.AppWorkspace); + } + } + } + + // Redraw requested + private void redrawtimer_Tick(object sender, EventArgs e) + { + // Disable timer (only redraw once) + redrawtimer.Enabled = false; + + // Resume control layouts + //if(displayresized) General.LockWindowUpdate(IntPtr.Zero); + + // Map opened? + if (General.Map != null) + { + // Display was resized? + if (displayresized) + { + // Reset graphics to match changes + General.Map.Graphics.Reset(); + } + + // This is a dirty trick to give the display a new mousemove event with correct arguments + if (mouseinside) + { + Point mousepos = Cursor.Position; + Cursor.Position = new Point(mousepos.X + 1, mousepos.Y + 1); + Cursor.Position = mousepos; + } + + // Redraw now + RedrawDisplay(); + } + + // Display resize is done + displayresized = false; + } + + // Display size changes + private void display_Resize(object sender, EventArgs e) + { + // Resizing + //if(!displayresized) General.LockWindowUpdate(display.Handle); + displayresized = true; + + // Request redraw + if (!redrawtimer.Enabled) redrawtimer.Enabled = true; + } + + // This requests a delayed redraw + public void DelayedRedraw() + { + // Request redraw + if (!redrawtimer.Enabled) redrawtimer.Enabled = true; + } + + // Mouse click + private void display_MouseClick(object sender, MouseEventArgs e) + { + if ((General.Map != null) && (General.Editing.Mode != null)) General.Editing.Mode.OnMouseClick(e); + } + + // Mouse doubleclick + private void display_MouseDoubleClick(object sender, MouseEventArgs e) + { + if ((General.Map != null) && (General.Editing.Mode != null)) General.Editing.Mode.OnMouseDoubleClick(e); + } + + // Mouse down + private void display_MouseDown(object sender, MouseEventArgs e) + { + int key = 0; + + LoseFocus(this, EventArgs.Empty); + + int mod = 0; + if (alt) mod |= (int)Keys.Alt; + if (shift) mod |= (int)Keys.Shift; + if (ctrl) mod |= (int)Keys.Control; + + // Apply button + mousebuttons |= e.Button; + + // Create key + switch (e.Button) + { + case MouseButtons.Left: key = (int)Keys.LButton; break; + case MouseButtons.Middle: key = (int)Keys.MButton; break; + case MouseButtons.Right: key = (int)Keys.RButton; break; + case MouseButtons.XButton1: key = (int)Keys.XButton1; break; + case MouseButtons.XButton2: key = (int)Keys.XButton2; break; + } + + // Invoke any actions associated with this key + General.Actions.KeyPressed(key | mod); + + // Invoke on editing mode + if ((General.Map != null) && (General.Editing.Mode != null)) General.Editing.Mode.OnMouseDown(e); + } + + // Mouse enters + private void display_MouseEnter(object sender, EventArgs e) + { + mouseinside = true; + if ((General.Map != null) && (mouseinput == null) && (General.Editing.Mode != null)) General.Editing.Mode.OnMouseEnter(e); + } + + // Mouse leaves + private void display_MouseLeave(object sender, EventArgs e) + { + mouseinside = false; + if ((General.Map != null) && (mouseinput == null) && (General.Editing.Mode != null)) General.Editing.Mode.OnMouseLeave(e); + } + + // Mouse moves + private void display_MouseMove(object sender, MouseEventArgs e) + { + if ((General.Map != null) && (mouseinput == null) && (General.Editing.Mode != null)) General.Editing.Mode.OnMouseMove(e); + } + + // Mouse up + private void display_MouseUp(object sender, MouseEventArgs e) + { + int key = 0; + + int mod = 0; + if (alt) mod |= (int)Keys.Alt; + if (shift) mod |= (int)Keys.Shift; + if (ctrl) mod |= (int)Keys.Control; + + // Apply button + mousebuttons &= ~e.Button; + + // Create key + switch (e.Button) + { + case MouseButtons.Left: key = (int)Keys.LButton; break; + case MouseButtons.Middle: key = (int)Keys.MButton; break; + case MouseButtons.Right: key = (int)Keys.RButton; break; + case MouseButtons.XButton1: key = (int)Keys.XButton1; break; + case MouseButtons.XButton2: key = (int)Keys.XButton2; break; + } + + // Invoke any actions associated with this key + General.Actions.KeyReleased(key | mod); + + // Invoke on editing mode + if ((General.Map != null) && (General.Editing.Mode != null)) General.Editing.Mode.OnMouseUp(e); + } + + #endregion + + #region ================== Input + + // This is a tool to lock the mouse in exclusive mode + private void StartMouseExclusive() + { + // Not already locked? + if (mouseinput == null) + { + // Start special input device + mouseinput = new MouseInput(this); + + // Lock and hide the mouse in window + originalclip = Cursor.Clip; + Cursor.Clip = display.RectangleToScreen(display.ClientRectangle); + Cursor.Hide(); + } + } + + // This is a tool to unlock the mouse + private void StopMouseExclusive() + { + // Locked? + if (mouseinput != null) + { + // Stop special input device + mouseinput.Dispose(); + mouseinput = null; + + // Release and show the mouse + Cursor.Clip = originalclip; + Cursor.Position = display.PointToScreen(new Point(display.ClientSize.Width / 2, display.ClientSize.Height / 2)); + Cursor.Show(); + } + } + + // This requests exclusive mouse input + public void StartExclusiveMouseInput() + { + // Only when not already in exclusive mode + if (!mouseexclusive) + { + General.WriteLogLine("Starting exclusive mouse input mode..."); + + // Start special input device + StartMouseExclusive(); + mouseexclusive = true; + mouseexclusivebreaklevel = 0; + } + } + + // This stops exclusive mouse input + public void StopExclusiveMouseInput() + { + // Only when in exclusive mode + if (mouseexclusive) + { + General.WriteLogLine("Stopping exclusive mouse input mode..."); + + // Stop special input device + StopMouseExclusive(); + mouseexclusive = false; + mouseexclusivebreaklevel = 0; + } + } + + // This temporarely breaks exclusive mode and counts the break level + public void BreakExclusiveMouseInput() + { + // Only when in exclusive mode + if (mouseexclusive) + { + // Stop special input device + StopMouseExclusive(); + + // Count the break level + mouseexclusivebreaklevel++; + } + } + + // This resumes exclusive mode from a break when all breaks have been called to resume + public void ResumeExclusiveMouseInput() + { + // Only when in exclusive mode + if (mouseexclusive && (mouseexclusivebreaklevel > 0)) + { + // Decrease break level + mouseexclusivebreaklevel--; + + // All break levels resumed? Then lock the mouse again. + if (mouseexclusivebreaklevel == 0) + StartMouseExclusive(); + } + } + + // This releases all keys + internal void ReleaseAllKeys() + { + General.Actions.ReleaseAllKeys(); + mousebuttons = MouseButtons.None; + shift = false; + ctrl = false; + alt = false; + } + + // When the mouse wheel is changed + protected override void OnMouseWheel(MouseEventArgs e) + { + int mod = 0; + if (alt) mod |= (int)Keys.Alt; + if (shift) mod |= (int)Keys.Shift; + if (ctrl) mod |= (int)Keys.Control; + + // Scrollwheel up? + if (e.Delta > 0) + { + // Invoke actions for scrollwheel + //for(int i = 0; i < e.Delta; i += 120) + General.Actions.KeyPressed((int)SpecialKeys.MScrollUp | mod); + General.Actions.KeyReleased((int)SpecialKeys.MScrollUp | mod); + } + // Scrollwheel down? + else if (e.Delta < 0) + { + // Invoke actions for scrollwheel + //for(int i = 0; i > e.Delta; i -= 120) + General.Actions.KeyPressed((int)SpecialKeys.MScrollDown | mod); + General.Actions.KeyReleased((int)SpecialKeys.MScrollDown | mod); + } + + // Let the base know + base.OnMouseWheel(e); + } + + // When a key is pressed + private void MainForm_KeyDown(object sender, KeyEventArgs e) + { + int mod = 0; + + // Keep key modifiers + alt = e.Alt; + shift = e.Shift; + ctrl = e.Control; + if (alt) mod |= (int)Keys.Alt; + if (shift) mod |= (int)Keys.Shift; + if (ctrl) mod |= (int)Keys.Control; + + // Don't process any keys when they are meant for other input controls + if ((ActiveControl == null) || (ActiveControl == display)) + { + // Invoke any actions associated with this key + General.Actions.UpdateModifiers(mod); + General.Actions.KeyPressed((int)e.KeyData); + + // Invoke on editing mode + if ((General.Map != null) && (General.Editing.Mode != null)) General.Editing.Mode.OnKeyDown(e); + + // Handled + e.Handled = true; + e.SuppressKeyPress = true; + } + + // F1 pressed? + if ((e.KeyCode == Keys.F1) && (e.Modifiers == Keys.None)) + { + // No action bound to F1? + Actions.Action[] f1actions = General.Actions.GetActionsByKey((int)e.KeyData); + if (f1actions.Length == 0) + { + // If we don't have any map open, show the Main Window help + // otherwise, give the help request to the editing mode so it + // can open the appropriate help file. + if ((General.Map == null) || (General.Editing.Mode == null)) + { + General.ShowHelp("introduction.html"); + } + else + { + General.Editing.Mode.OnHelp(); + } + } + } + } + + // When a key is released + private void MainForm_KeyUp(object sender, KeyEventArgs e) + { + int mod = 0; + + // Keep key modifiers + alt = e.Alt; + shift = e.Shift; + ctrl = e.Control; + if (alt) mod |= (int)Keys.Alt; + if (shift) mod |= (int)Keys.Shift; + if (ctrl) mod |= (int)Keys.Control; + + // Don't process any keys when they are meant for other input controls + if ((ActiveControl == null) || (ActiveControl == display)) + { + // Invoke any actions associated with this key + General.Actions.UpdateModifiers(mod); + General.Actions.KeyReleased((int)e.KeyData); + + // Invoke on editing mode + if ((General.Map != null) && (General.Editing.Mode != null)) General.Editing.Mode.OnKeyUp(e); + + // Handled + e.Handled = true; + e.SuppressKeyPress = true; + } + } + + // These prevent focus changes by way of TAB or Arrow keys + protected override bool IsInputChar(char charCode) { return false; } + protected override bool IsInputKey(Keys keyData) { return false; } + protected override bool ProcessKeyPreview(ref Message m) { return false; } + protected override bool ProcessDialogKey(Keys keyData) { return false; } + protected override bool ProcessCmdKey(ref Message msg, Keys keyData) { return false; } + + // This fixes some odd input behaviour + private void display_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e) + { + if ((ActiveControl == null) || (ActiveControl == display)) + { + LoseFocus(this, EventArgs.Empty); + KeyEventArgs ea = new KeyEventArgs(e.KeyData); + MainForm_KeyDown(sender, ea); + } + } + + #endregion + + #region ================== Toolbar + + // This updates the skills list + private void UpdateSkills() + { + // Clear list + buttontest.DropDownItems.Clear(); + + // Map loaded? + if (General.Map != null) + { + // Make the new skills list + skills = new ToolStripItem[(General.Map.Config.Skills.Count * 2) + 1]; + int addindex = 0; + + // Positive skills are with monsters + for (int i = 0; i < General.Map.Config.Skills.Count; i++) + { + ToolStripMenuItem menuitem = new ToolStripMenuItem(General.Map.Config.Skills[i].ToString()); + menuitem.Image = Properties.Resources.Monster2; + menuitem.Click += new EventHandler(TestSkill_Click); + menuitem.Tag = General.Map.Config.Skills[i].Index; + menuitem.Checked = (General.Settings.TestMonsters && (General.Map.ConfigSettings.TestSkill == General.Map.Config.Skills[i].Index)); + skills[addindex++] = menuitem; + } + + // Add seperator + skills[addindex] = new ToolStripSeparator(); + skills[addindex].Padding = new Padding(0, 3, 0, 3); + addindex++; + + // Negative skills are without monsters + for (int i = 0; i < General.Map.Config.Skills.Count; i++) + { + ToolStripMenuItem menuitem = new ToolStripMenuItem(General.Map.Config.Skills[i].ToString()); + menuitem.Image = Properties.Resources.Monster3; + menuitem.Click += new EventHandler(TestSkill_Click); + menuitem.Tag = -General.Map.Config.Skills[i].Index; + menuitem.Checked = (!General.Settings.TestMonsters && (General.Map.ConfigSettings.TestSkill == General.Map.Config.Skills[i].Index)); + skills[addindex++] = menuitem; + } + + // Add to list + buttontest.DropDownItems.AddRange(skills); + } + } + + // Event handler for testing at a specific skill + private void TestSkill_Click(object sender, EventArgs e) + { + int skill = (int)((sender as ToolStripMenuItem).Tag); + General.Settings.TestMonsters = (skill > 0); + General.Map.ConfigSettings.TestSkill = Math.Abs(skill); + General.Map.Launcher.TestAtSkill(Math.Abs(skill)); + UpdateSkills(); + } + + // This loses focus + private void LoseFocus(object sender, EventArgs e) + { + // Lose focus! + try { display.Focus(); } catch (Exception) { } + this.ActiveControl = null; + } + + // Things filter selected + private void thingfilters_SelectedIndexChanged(object sender, EventArgs e) + { + // Only possible when a map is open + if ((General.Map != null) && !updatingfilters) + { + updatingfilters = true; + + // Change filter + General.Map.ChangeThingFilter(thingfilters.SelectedItem as ThingsFilter); + + updatingfilters = false; + } + + // Lose focus + if (!thingfilters.DroppedDown) LoseFocus(sender, e); + } + + // This updates the things filter on the toolbar + internal void UpdateThingsFilters() + { + // Only possible to list filters when a map is open + if (General.Map != null) + { + ThingsFilter oldfilter = null; + if (thingfilters.SelectedIndex > -1) + oldfilter = thingfilters.SelectedItem as ThingsFilter; + + updatingfilters = true; + + // Clear the list + thingfilters.Items.Clear(); + + // Add null filter + if (General.Map.ThingsFilter is NullThingsFilter) + thingfilters.Items.Add(General.Map.ThingsFilter); + else + thingfilters.Items.Add(new NullThingsFilter()); + + // Add all filters + foreach (ThingsFilter f in General.Map.ConfigSettings.ThingsFilters) + thingfilters.Items.Add(f); + + // Select current filter + foreach (ThingsFilter f in thingfilters.Items) + if (f == General.Map.ThingsFilter) thingfilters.SelectedItem = f; + + updatingfilters = false; + + // No filter selected? + if (thingfilters.SelectedIndex == -1) + { + // Select the first and update + thingfilters.SelectedIndex = 0; + } + // Another filter got selected? + else if (oldfilter != (thingfilters.SelectedItem as ThingsFilter)) + { + // Update! + thingfilters_SelectedIndexChanged(this, EventArgs.Empty); + } + } + else + { + // Clear the list + thingfilters.Items.Clear(); + } + } + + // This selects the things filter based on the filter set on the map manager + internal void ReflectThingsFilter() + { + if (!updatingfilters) + { + updatingfilters = true; + + // Select current filter + bool selecteditemfound = false; + foreach (ThingsFilter f in thingfilters.Items) + { + if (f == General.Map.ThingsFilter) + { + thingfilters.SelectedItem = f; + selecteditemfound = true; + } + } + + // Not in the list? + if (!selecteditemfound) + { + // Select nothing + thingfilters.SelectedIndex = -1; + } + + updatingfilters = false; + } + } + + // This adds a button to the toolbar + public void AddButton(ToolStripItem button) + { + // Find the plugin that called this method + Plugin plugin = General.Plugins.FindPluginByAssembly(Assembly.GetCallingAssembly()); + + // Fix tags to full action names + ToolStripItemCollection items = new ToolStripItemCollection(toolbar, new ToolStripItem[0]); + items.Add(button); + RenameTagsToFullActions(items, plugin); + + // Bind visible changed event + if (!(button is ToolStripSeparator)) button.VisibleChanged += buttonvisiblechangedhandler; + + // Insert the button at the end of the toolbar + toolbar.Items.Add(button); + UpdateSeparators(); + } + + // Removes a button + public void RemoveButton(ToolStripItem button) + { + // Unbind visible changed event + if (!(button is ToolStripSeparator)) button.VisibleChanged -= buttonvisiblechangedhandler; + + // Remove button + toolbar.Items.Remove(button); + UpdateSeparators(); + } + + // This handle visibility changes in the toolbar buttons + private void ToolbarButtonVisibleChanged(object sender, EventArgs e) + { + // Update the seeprators + UpdateSeparators(); + } + + // This hides redundant seperators and shows single seperators + internal void UpdateSeparators() + { + UpdateToolStripSeparators(toolbar.Items, false); + UpdateToolStripSeparators(menumode.DropDownItems, true); + } + + // This updates the seperators + // Hides redundant seperators and shows single seperators + private void UpdateToolStripSeparators(ToolStripItemCollection items, bool defaultvisible) + { + ToolStripItem pvi = null; + foreach (ToolStripItem i in items) + { + bool separatorvisible = false; + + // This is a seperator? + if (i is ToolStripSeparator) + { + // Make visible when previous item was not a seperator + separatorvisible = !(pvi is ToolStripSeparator) && (pvi != null); + i.Visible = separatorvisible; + } + + // Keep as previous visible item + if (i.Visible || separatorvisible || (defaultvisible && !(i is ToolStripSeparator))) pvi = i; + } + + // Hide last item if it is a seperator + if (pvi is ToolStripSeparator) pvi.Visible = false; + } + + // This enables or disables all editing mode items and toolbar buttons + private void UpdateToolbar() + { + // Enable/disable all edit mode items + foreach (ToolStripItem i in editmodeitems) i.Enabled = (General.Map != null); + } + + // This checks one of the edit mode items (and unchecks all others) + internal void CheckEditModeButton(string modeclassname) + { + // Go for all items + foreach (ToolStripItem i in editmodeitems) + { + // Check what type it is + if (i is ToolStripMenuItem) + { + // Check if mode type matches with given name + (i as ToolStripMenuItem).Checked = ((i.Tag as EditModeInfo).Type.Name == modeclassname); + } + else if (i is ToolStripButton) + { + // Check if mode type matches with given name + (i as ToolStripButton).Checked = ((i.Tag as EditModeInfo).Type.Name == modeclassname); + } + } + } + + // This removes the config-specific editing mode buttons + internal void RemoveEditModeButtons() + { + // Go for all items + foreach (ToolStripItem i in editmodeitems) + { + // Remove it and restart + toolbar.Items.Remove(i); + menumode.DropDownItems.Remove(i); + i.Dispose(); + } + + // Done + editmodeitems.Clear(); + UpdateSeparators(); + } + + // This adds an editing mode seperator on the toolbar and menu + internal void AddEditModeSeperator() + { + ToolStripSeparator item; + int index; + + // Create a button + index = toolbar.Items.IndexOf(buttoneditmodesseperator); + item = new ToolStripSeparator(); + item.Margin = new Padding(6, 0, 6, 0); + toolbar.Items.Insert(index, item); + editmodeitems.Add(item); + + // Create menu item + index = menumode.DropDownItems.Count; + item = new ToolStripSeparator(); + item.Margin = new Padding(0, 3, 0, 3); + menumode.DropDownItems.Insert(index, item); + editmodeitems.Add(item); + + UpdateSeparators(); + } + + // This adds an editing mode button to the toolbar and edit menu + internal void AddEditModeButton(EditModeInfo modeinfo) + { + ToolStripItem item; + int index; + + string controlname = modeinfo.ButtonDesc.Replace("&", "&&"); + + // Create a button + index = toolbar.Items.IndexOf(buttoneditmodesseperator); + item = new ToolStripButton(modeinfo.ButtonDesc, modeinfo.ButtonImage, new EventHandler(EditModeButtonHandler)); + item.DisplayStyle = ToolStripItemDisplayStyle.Image; + item.Tag = modeinfo; + toolbar.Items.Insert(index, item); + editmodeitems.Add(item); + + // Create menu item + index = menumode.DropDownItems.Count; + item = new ToolStripMenuItem(controlname, modeinfo.ButtonImage, new EventHandler(EditModeButtonHandler)); + item.Tag = modeinfo; + menumode.DropDownItems.Insert(index, item); + editmodeitems.Add(item); + item.Visible = true; + + ApplyShortcutKeys(menumode.DropDownItems); + UpdateSeparators(); + } + + // This handles edit mode button clicks + private void EditModeButtonHandler(object sender, EventArgs e) + { + EditModeInfo modeinfo; + + this.Update(); + modeinfo = (EditModeInfo)((sender as ToolStripItem).Tag); + General.Actions.InvokeAction(modeinfo.SwitchAction.GetFullActionName(modeinfo.Plugin.Assembly)); + this.Update(); + } + + #endregion + + #region ================== Menus + + // This adds a menu to the menus bar + public void AddMenu(ToolStripMenuItem menu) + { + // Find the plugin that called this method + Plugin plugin = General.Plugins.FindPluginByAssembly(Assembly.GetCallingAssembly()); + + // Fix tags to full action names + RenameTagsToFullActions(menu.DropDownItems, plugin); + + // Insert the menu before the Tools menu + menumain.Items.Insert(menumain.Items.IndexOf(menutools), menu); + ApplyShortcutKeys(menu.DropDownItems); + } + + // Removes a menu + public void RemoveMenu(ToolStripMenuItem menu) + { + menumain.Items.Remove(menu); + } + + // Public method to apply shortcut keys + internal void ApplyShortcutKeys() + { + // Apply shortcut keys to menus + ApplyShortcutKeys(menumain.Items); + } + + // This sets the shortcut keys on menu items + private void ApplyShortcutKeys(ToolStripItemCollection items) + { + // Go for all controls to find menu items + foreach (ToolStripItem item in items) + { + // This is a menu item? + if (item is ToolStripMenuItem) + { + // Get the item in proper type + ToolStripMenuItem menuitem = (item as ToolStripMenuItem); + + // Tag set for this item? + if (menuitem.Tag is string) + { + // Action with this name available? + string actionname = menuitem.Tag.ToString(); + if (General.Actions.Exists(actionname)) + { + // Put the action shortcut key on the menu item + menuitem.ShortcutKeyDisplayString = Actions.Action.GetShortcutKeyDesc(General.Actions[actionname].ShortcutKey); + } + } + // Edit mode info set for this item? + else if (menuitem.Tag is EditModeInfo) + { + // Action with this name available? + EditModeInfo modeinfo = (menuitem.Tag as EditModeInfo); + string actionname = modeinfo.SwitchAction.GetFullActionName(modeinfo.Plugin.Assembly); + if (General.Actions.Exists(actionname)) + { + // Put the action shortcut key on the menu item + menuitem.ShortcutKeyDisplayString = Actions.Action.GetShortcutKeyDesc(General.Actions[actionname].ShortcutKey); + } + } + + // Recursively apply shortcut keys to child menu items as well + ApplyShortcutKeys(menuitem.DropDownItems); + } + } + } + + // This fixes short action names to fully qualified + // action names on menu item tags + private void RenameTagsToFullActions(ToolStripItemCollection items, Plugin plugin) + { + // Go for all controls to find menu items + foreach (ToolStripItem item in items) + { + // Tag set for this item? + if ((item.Tag != null) && (item.Tag is string)) + { + // Get the action name + string actionname = item.Tag.ToString(); + + // Check if the tag doe not already begin with the assembly name + if (!(item.Tag as string).StartsWith(plugin.Name + "_", StringComparison.InvariantCultureIgnoreCase)) + { + // Change the tag to a fully qualified action name + item.Tag = plugin.Name.ToLowerInvariant() + "_" + (item.Tag as string); + } + } + + // This is a menu item? + if (item is ToolStripMenuItem) + { + // Get the item in proper type + ToolStripMenuItem menuitem = (item as ToolStripMenuItem); + + // Recursively perform operation on child menu items + RenameTagsToFullActions(menuitem.DropDownItems, plugin); + } + } + } + + #endregion + + #region ================== File Menu + + // This sets up the file menu + private void UpdateFileMenu() + { + // Enable/disable items + itemclosemap.Enabled = (General.Map != null); + itemsavemap.Enabled = (General.Map != null); + itemsavemapas.Enabled = (General.Map != null); + itemsavemapinto.Enabled = (General.Map != null); + itemtestmap.Enabled = (General.Map != null); + + // Toolbar icons + buttonnewmap.Enabled = itemnewmap.Enabled; + buttonopenmap.Enabled = itemopenmap.Enabled; + buttonsavemap.Enabled = itemsavemap.Enabled; + buttontest.Enabled = itemtestmap.Enabled; + } + + // This sets the recent files from configuration + private void CreateRecentFiles() + { + int insertindex; + bool anyitems = false; + string filename; + + // Where to insert + insertindex = menufile.DropDownItems.IndexOf(itemnorecent); + + // Create all items + recentitems = new ToolStripMenuItem[MAX_RECENT_FILES]; + for (int i = 0; i < MAX_RECENT_FILES; i++) + { + // Create item + recentitems[i] = new ToolStripMenuItem(""); + recentitems[i].Tag = ""; + recentitems[i].Click += new EventHandler(recentitem_Click); + menufile.DropDownItems.Insert(insertindex + i, recentitems[i]); + + // Get configuration setting + filename = General.Settings.ReadSetting("recentfiles.file" + i, ""); + if (filename != "") + { + // Set up item + recentitems[i].Text = GetDisplayFilename(filename); + recentitems[i].Tag = filename; + recentitems[i].Visible = true; + anyitems = true; + } + else + { + // Hide item + recentitems[i].Visible = false; + } + } + + // Hide the no recent item when there are items + itemnorecent.Visible = !anyitems; + } + + // This saves the recent files list + private void SaveRecentFiles() + { + // Go for all items + for (int i = 0; i < MAX_RECENT_FILES; i++) + { + // Recent file set? + if (recentitems[i].Text != "") + { + // Save to configuration + General.Settings.WriteSetting("recentfiles.file" + i, recentitems[i].Tag.ToString()); + } + } + } + + // This adds a recent file to the list + internal void AddRecentFile(string filename) + { + int movedownto = MAX_RECENT_FILES - 1; + + // Check if this file is already in the list + for (int i = 0; i < MAX_RECENT_FILES; i++) + { + // File same as this item? + if (string.Compare(filename, recentitems[i].Tag.ToString(), true) == 0) + { + // Move down to here so that this item will disappear + movedownto = i; + break; + } + } + + // Go for all items, except the last one, backwards + for (int i = movedownto - 1; i >= 0; i--) + { + // Move recent file down the list + recentitems[i + 1].Text = recentitems[i].Text; + recentitems[i + 1].Tag = recentitems[i].Tag.ToString(); + recentitems[i + 1].Visible = (recentitems[i + 1].Text != ""); + } + + // Add new file at the top + recentitems[0].Text = GetDisplayFilename(filename); + recentitems[0].Tag = filename; + recentitems[0].Visible = true; + + // Hide the no recent item + itemnorecent.Visible = false; + } + + // This returns the trimmed file/path string + private string GetDisplayFilename(string filename) + { + string newname; + + // String doesnt fit? + if (GetStringWidth(filename) > MAX_RECENT_FILES_PIXELS) + { + // Start chopping off characters + for (int i = filename.Length - 6; i >= 0; i--) + { + // Does it fit now? + newname = filename.Substring(0, 3) + "..." + filename.Substring(filename.Length - i, i); + if (GetStringWidth(newname) <= MAX_RECENT_FILES_PIXELS) return newname; + } + + // Cant find anything that fits (most unlikely!) + return "wtf?!"; + } + else + { + // The whole string fits + return filename; + } + } + + // This returns the width of a string + private float GetStringWidth(string str) + { + Graphics g = Graphics.FromHwndInternal(this.Handle); + SizeF strsize = g.MeasureString(str, this.Font); + return strsize.Width; + } + + // Exit clicked + private void itemexit_Click(object sender, EventArgs e) { this.Close(); } + + // Recent item clicked + private void recentitem_Click(object sender, EventArgs e) + { + // Get the item that was clicked + ToolStripItem item = (sender as ToolStripItem); + + // Open this file + General.OpenMapFile(item.Tag.ToString()); + } + + #endregion + + #region ================== Edit Menu + + // This sets up the edit menu + private void UpdateEditMenu() + { + // No edit menu when no map open + //menuedit.Visible = (General.Map != null); + + // Enable/disable items + itemundo.Enabled = (General.Map != null) && (General.Map.UndoRedo.NextUndo != null); + itemredo.Enabled = (General.Map != null) && (General.Map.UndoRedo.NextRedo != null); + itemcut.Enabled = (General.Map != null) && (General.Editing.Mode != null) && General.Editing.Mode.Attributes.AllowCopyPaste; + itemcopy.Enabled = (General.Map != null) && (General.Editing.Mode != null) && General.Editing.Mode.Attributes.AllowCopyPaste; + itempaste.Enabled = (General.Map != null) && (General.Editing.Mode != null) && General.Editing.Mode.Attributes.AllowCopyPaste; + itempastespecial.Enabled = (General.Map != null) && (General.Editing.Mode != null) && General.Editing.Mode.Attributes.AllowCopyPaste; + itemmapoptions.Enabled = (General.Map != null); + itemsnaptogrid.Enabled = (General.Map != null); + itemautomerge.Enabled = (General.Map != null); + itemfullbrightness.Enabled = (General.Map != null); + itemgridsetup.Enabled = (General.Map != null); + itemgridinc.Enabled = (General.Map != null); + itemgriddec.Enabled = (General.Map != null); + + // Determine undo description + if (itemundo.Enabled) + itemundo.Text = "Undo " + General.Map.UndoRedo.NextUndo.Description; + else + itemundo.Text = "Undo"; + + // Determine redo description + if (itemredo.Enabled) + itemredo.Text = "Redo " + General.Map.UndoRedo.NextRedo.Description; + else + itemredo.Text = "Redo"; + + // Toolbar icons + buttonmapoptions.Enabled = (General.Map != null); + buttonundo.Enabled = itemundo.Enabled; + buttonredo.Enabled = itemredo.Enabled; + buttonundo.ToolTipText = itemundo.Text; + buttonredo.ToolTipText = itemredo.Text; + buttonsnaptogrid.Enabled = (General.Map != null); + buttonautomerge.Enabled = (General.Map != null); + buttonfullbrightness.Enabled = (General.Map != null); + buttonfullbrightness.Checked = Renderer.FullBrightness; + buttoncut.Enabled = itemcut.Enabled; + buttoncopy.Enabled = itemcopy.Enabled; + buttonpaste.Enabled = itempaste.Enabled; + } + + // Action to toggle snap to grid + [BeginAction("togglesnap")] + internal void ToggleSnapToGrid() + { + buttonsnaptogrid.Checked = !buttonsnaptogrid.Checked; + itemsnaptogrid.Checked = buttonsnaptogrid.Checked; + string onoff = buttonsnaptogrid.Checked ? "ON" : "OFF"; + DisplayStatus(StatusType.Action, "Snap to grid is now " + onoff + " by default."); + } + + // Action to toggle auto merge + [BeginAction("toggleautomerge")] + internal void ToggleAutoMerge() + { + buttonautomerge.Checked = !buttonautomerge.Checked; + itemautomerge.Checked = buttonautomerge.Checked; + string onoff = buttonautomerge.Checked ? "ON" : "OFF"; + DisplayStatus(StatusType.Action, "Snap to geometry is now " + onoff + " by default."); + } + + [BeginAction("togglefullbrightness")] + public void ToggleFullBrightness() + { + Renderer.FullBrightness = !Renderer.FullBrightness; + buttonfullbrightness.Checked = Renderer.FullBrightness; + itemfullbrightness.Checked = Renderer.FullBrightness; + General.Interface.DisplayStatus(StatusType.Action, "Full Brightness is now " + (Renderer.FullBrightness ? "ON" : "OFF")); + + // Redraw display to show changes + General.Interface.RedrawDisplay(); + } + + #endregion + + #region ================== View Menu + + // This sets up the modes menu + private void UpdateViewMenu() + { + // Menu items + itemthingsfilter.Enabled = (General.Map != null); + itemscripteditor.Enabled = (General.Map != null); + itemfittoscreen.Enabled = (General.Map != null); + menuzoom.Enabled = (General.Map != null); + itemtoggleinfo.Checked = IsInfoPanelExpanded; + + // View mode items + for (int i = 0; i < Renderer2D.NUM_VIEW_MODES; i++) + { + // NOTE: We only disable them when no map is loaded, because they may + // need to be disabled for non-classic modes + if (General.Map == null) + { + viewmodesbuttons[i].Enabled = false; + viewmodesbuttons[i].Checked = false; + viewmodesitems[i].Enabled = false; + viewmodesitems[i].Checked = false; + } + else + { + // Check the correct item + viewmodesbuttons[i].Checked = (i == (int)General.Map.CRenderer2D.ViewMode); + viewmodesitems[i].Checked = (i == (int)General.Map.CRenderer2D.ViewMode); + } + } + + // Toolbar icons + thingfilters.Enabled = (General.Map != null); + buttonthingsfilter.Enabled = (General.Map != null); + buttonscripteditor.Enabled = (General.Map != null); + } + + #endregion + + #region ================== Mode Menu + + // This sets up the modes menu + private void UpdateModeMenu() + { + menumode.Visible = (General.Map != null); + } + + #endregion + + #region ================== Help Menu + + // This sets up the help menu + private void UpdateHelpMenu() + { + itemhelpeditmode.Enabled = ((General.Map != null) && (General.Editing.Mode != null)); + } + + // About clicked + private void itemhelpabout_Click(object sender, EventArgs e) + { + AboutForm aboutform; + + // Show about dialog + aboutform = new AboutForm(); + aboutform.ShowDialog(this); + } + + // Reference Manual clicked + private void itemhelprefmanual_Click(object sender, EventArgs e) + { + General.ShowHelp("introduction.html"); + } + + // About this Editing Mode + private void itemhelpeditmode_Click(object sender, EventArgs e) + { + if ((General.Map != null) && (General.Editing.Mode != null)) + General.Editing.Mode.OnHelp(); + } + + #endregion + + #region ================== Prefabs Menu + + // This sets up the prefabs menu + private void UpdatePrefabsMenu() + { + // Enable/disable items + itemcreateprefab.Enabled = (General.Map != null); + iteminsertprefabfile.Enabled = (General.Map != null); + iteminsertpreviousprefab.Enabled = (General.Map != null) && General.Map.CopyPaste.IsPreviousPrefabAvailable; + + // Toolbar icons + buttoninsertprefabfile.Enabled = (General.Map != null); + buttoninsertpreviousprefab.Enabled = (General.Map != null) && General.Map.CopyPaste.IsPreviousPrefabAvailable; + } + + #endregion + + #region ================== Tools Menu + + // This sets up the tools menu + private void UpdateToolsMenu() + { + // Enable/disable items + itemreloadresources.Enabled = (General.Map != null); + } + + // Errors and Warnings + [BeginAction("showerrors")] + internal void ShowErrors() + { + ErrorsForm errform = new ErrorsForm(); + errform.ShowDialog(this); + errform.Dispose(); + } + + // Game Configuration action + [BeginAction("configuration")] + internal void ShowConfiguration() + { + // Show configuration dialog + ShowConfigurationPage(-1); + } + + // This shows the configuration on a specific page + internal void ShowConfigurationPage(int pageindex) + { + // Show configuration dialog + ConfigForm cfgform = new ConfigForm(); + if (pageindex > -1) cfgform.ShowTab(pageindex); + if (cfgform.ShowDialog(this) == DialogResult.OK) + { + // Update stuff + SetupInterface(); + UpdateInterface(); + General.Editing.UpdateCurrentEditModes(); + General.Plugins.ProgramReconfigure(); + + // Reload resources if a map is open + if ((General.Map != null) && cfgform.ReloadResources) General.Actions.InvokeAction("builder_reloadresources"); + + // Redraw display + RedrawDisplay(); + } + + // Done + cfgform.Dispose(); + } + + // Preferences action + [BeginAction("preferences")] + internal void ShowPreferences() + { + // Show preferences dialog + PreferencesForm prefform = new PreferencesForm(); + if (prefform.ShowDialog(this) == DialogResult.OK) + { + // Update stuff + SetupInterface(); + ApplyShortcutKeys(); + General.Colors.CreateCorrectionTable(); + General.Plugins.ProgramReconfigure(); + + // Map opened? + if (General.Map != null) + { + // Reload resources! + if (General.Map.ScriptEditor != null) General.Map.ScriptEditor.Editor.RefreshSettings(); + General.Map.Graphics.SetupSettings(); + General.Map.UpdateConfiguration(); + if (prefform.ReloadResources) General.Actions.InvokeAction("builder_reloadresources"); + } + + // Redraw display + RedrawDisplay(); + } + + // Done + prefform.Dispose(); + } + + #endregion + + #region ================== Info Panels + + // This toggles the panel expanded / collapsed + [BeginAction("toggleinfopanel")] + internal void ToggleInfoPanel() + { + if (IsInfoPanelExpanded) + { + panelinfo.Height = buttontoggleinfo.Height + buttontoggleinfo.Top; + buttontoggleinfo.Text = "5"; // Arrow up + if (linedefinfo.Visible) linedefinfo.Hide(); + if (vertexinfo.Visible) vertexinfo.Hide(); + if (sectorinfo.Visible) sectorinfo.Hide(); + if (thinginfo.Visible) thinginfo.Hide(); + modename.Visible = false; + labelcollapsedinfo.Visible = true; + itemtoggleinfo.Checked = false; + } + else + { + panelinfo.Height = heightpanel1.Height; + buttontoggleinfo.Text = "6"; // Arrow down + labelcollapsedinfo.Visible = false; + itemtoggleinfo.Checked = true; + if (lastinfoobject is Vertex) ShowVertexInfo(lastinfoobject as Vertex); + else if (lastinfoobject is Linedef) ShowLinedefInfo(lastinfoobject as Linedef); + else if (lastinfoobject is Sector) ShowSectorInfo(lastinfoobject as Sector); + else if (lastinfoobject is Thing) ShowThingInfo(lastinfoobject as Thing); + else HideInfo(); + } + + FocusDisplay(); + } + + // Mouse released on info panel toggle button + private void buttontoggleinfo_MouseUp(object sender, MouseEventArgs e) + { + FocusDisplay(); + } + + // This displays the current mode name + internal void DisplayModeName(string name) + { + if (lastinfoobject == null) + { + labelcollapsedinfo.Text = name; + labelcollapsedinfo.Refresh(); + } + modename.Text = name; + modename.Refresh(); + } + + // This hides all info panels + public void HideInfo() + { + // Hide them all + lastinfoobject = null; + if (linedefinfo.Visible) linedefinfo.Hide(); + if (vertexinfo.Visible) vertexinfo.Hide(); + if (sectorinfo.Visible) sectorinfo.Hide(); + if (thinginfo.Visible) thinginfo.Hide(); + labelcollapsedinfo.Text = modename.Text; + labelcollapsedinfo.Refresh(); + modename.Visible = ((General.Map != null) && IsInfoPanelExpanded); + modename.Refresh(); + } + + // This refreshes info + public void RefreshInfo() + { + if (lastinfoobject is Vertex) ShowVertexInfo(lastinfoobject as Vertex); + else if (lastinfoobject is Linedef) ShowLinedefInfo(lastinfoobject as Linedef); + else if (lastinfoobject is Sector) ShowSectorInfo(lastinfoobject as Sector); + else if (lastinfoobject is Thing) ShowThingInfo(lastinfoobject as Thing); + } + + // Show linedef info + public void ShowLinedefInfo(Linedef l) + { + lastinfoobject = l; + modename.Visible = false; + if (vertexinfo.Visible) vertexinfo.Hide(); + if (sectorinfo.Visible) sectorinfo.Hide(); + if (thinginfo.Visible) thinginfo.Hide(); + if (IsInfoPanelExpanded) linedefinfo.ShowInfo(l); + + // Show info on collapsed label + if (General.Map.Config.LinedefActions.ContainsKey(l.Action)) + { + LinedefActionInfo act = General.Map.Config.LinedefActions[l.Action]; + labelcollapsedinfo.Text = act.ToString(); + } + else if (l.Action == 0) + labelcollapsedinfo.Text = l.Action.ToString() + " - None"; + else + { + // villsa + /*if (General.Map.FormatInterface.InDoom64Mode && + (l.Action >= 256 && l.Action <= 511)) + { + labelcollapsedinfo.Text = (l.Action - 255).ToString() + " - Macro"; + } + else + */ + labelcollapsedinfo.Text = l.Action.ToString() + " - Unknown"; + } + + labelcollapsedinfo.Refresh(); + } + + // Show vertex info + public void ShowVertexInfo(Vertex v) + { + lastinfoobject = v; + modename.Visible = false; + if (linedefinfo.Visible) linedefinfo.Hide(); + if (sectorinfo.Visible) sectorinfo.Hide(); + if (thinginfo.Visible) thinginfo.Hide(); + if (IsInfoPanelExpanded) vertexinfo.ShowInfo(v); + + // Show info on collapsed label + labelcollapsedinfo.Text = v.Position.x.ToString("0.##") + ", " + v.Position.y.ToString("0.##"); + labelcollapsedinfo.Refresh(); + } + + // Show sector info + public void ShowSectorInfo(Sector s) + { + lastinfoobject = s; + modename.Visible = false; + if (linedefinfo.Visible) linedefinfo.Hide(); + if (vertexinfo.Visible) vertexinfo.Hide(); + if (thinginfo.Visible) thinginfo.Hide(); + if (IsInfoPanelExpanded) sectorinfo.ShowInfo(s); + + // Show info on collapsed label + if (General.Map.Config.SectorEffects.ContainsKey(s.Effect)) + labelcollapsedinfo.Text = General.Map.Config.SectorEffects[s.Effect].ToString(); + else if (s.Effect == 0) + labelcollapsedinfo.Text = s.Effect.ToString() + " - Normal"; + else + labelcollapsedinfo.Text = s.Effect.ToString() + " - Unknown"; + + labelcollapsedinfo.Refresh(); + } + + // Show thing info + public void ShowThingInfo(Thing t) + { + lastinfoobject = t; + modename.Visible = false; + if (linedefinfo.Visible) linedefinfo.Hide(); + if (vertexinfo.Visible) vertexinfo.Hide(); + if (sectorinfo.Visible) sectorinfo.Hide(); + if (IsInfoPanelExpanded) thinginfo.ShowInfo(t); + + // Show info on collapsed label + ThingTypeInfo ti = General.Map.Data.GetThingInfo(t.Type); + labelcollapsedinfo.Text = t.Type + " - " + ti.Title; + labelcollapsedinfo.Refresh(); + } + + #endregion + + #region ================== Dialogs + + // This browses for a texture + // Returns the new texture name or the same texture name when cancelled + public string BrowseTexture(IWin32Window owner, string initialvalue) + { + return TextureBrowserForm.Browse(owner, initialvalue); + } + + // This browses for a flat + // Returns the new flat name or the same flat name when cancelled + public string BrowseFlat(IWin32Window owner, string initialvalue) + { + return FlatBrowserForm.Browse(owner, initialvalue); + } + + // This browses the lindef types + // Returns the new action or the same action when cancelled + public int BrowseLinedefActions(IWin32Window owner, int initialvalue) + { + return ActionBrowserForm.BrowseAction(owner, initialvalue); + } + + // This browses sector effects + // Returns the new effect or the same effect when cancelled + public int BrowseSectorEffect(IWin32Window owner, int initialvalue) + { + return EffectBrowserForm.BrowseEffect(owner, initialvalue); + } + + // This browses thing types + // Returns the new thing type or the same thing type when cancelled + public int BrowseThingType(IWin32Window owner, int initialvalue) + { + return ThingBrowserForm.BrowseThing(owner, initialvalue); + } + + // This shows the dialog to edit vertices + public DialogResult ShowEditVertices(ICollection vertices) + { + DialogResult result; + + // Show sector edit dialog + VertexEditForm f = new VertexEditForm(); + f.Setup(vertices); + result = f.ShowDialog(this); + f.Dispose(); + + return result; + } + + // This shows the dialog to edit lines + public DialogResult ShowEditLinedefs(ICollection lines) + { + DialogResult result; + + // Show line edit dialog + LinedefEditForm f = new LinedefEditForm(); + f.Setup(lines); + result = f.ShowDialog(this); + f.Dispose(); + + return result; + } + + // This shows the dialog to edit sectors + public DialogResult ShowEditSectors(ICollection sectors) + { + DialogResult result; + + // Show sector edit dialog + SectorEditForm f = new SectorEditForm(); + f.Setup(sectors); + result = f.ShowDialog(this); + f.Dispose(); + + return result; + } + + // This shows the dialog to edit things + public DialogResult ShowEditThings(ICollection things) + { + DialogResult result; + + // Show thing edit dialog + ThingEditForm f = new ThingEditForm(); + f.Setup(things); + result = f.ShowDialog(this); + f.Dispose(); + + return result; + } + + #endregion + + #region ================== Message Pump + + // This handles messages + protected override unsafe void WndProc(ref Message m) + { + // Notify message? + switch (m.Msg) + { + case (int)ThreadMessages.UpdateStatus: + DisplayStatus(status); + break; + + case (int)ThreadMessages.ImageDataLoaded: + string imagename = Marshal.PtrToStringAuto(m.WParam); + Marshal.FreeCoTaskMem(m.WParam); + if ((General.Map != null) && (General.Map.Data != null)) + { + ImageData img = General.Map.Data.GetFlatImage(imagename); + if (img != null) ImageDataLoaded(img); + } + break; + + case General.WM_SYSCOMMAND: + // We don't want to open a menu when ALT is pressed + if (m.WParam.ToInt32() != General.SC_KEYMENU) + { + base.WndProc(ref m); + } + break; + + default: + // Let the base handle the message + base.WndProc(ref m); + break; + } + } + + #endregion + + #region ================== Processing + + // This is called from the background thread when images are loaded + // but only when first loaded or when dimensions were changed + internal void ImageDataLoaded(ImageData img) + { + // Image is used in the map? + if ((img != null) && img.UsedInMap && !img.IsDisposed) + { + // Go for all setors + bool updated = false; + foreach (Sector s in General.Map.Map.Sectors) + { + // Update floor buffer if needed + if (s.LongFloorTexture == img.LongName) + { + s.UpdateFloorSurface(); + updated = true; + } + + // Update ceiling buffer if needed + if (s.LongCeilTexture == img.LongName) + { + s.UpdateCeilingSurface(); + updated = true; + } + } + + // If we made updates, redraw the screen + if (updated) DelayedRedraw(); + } + } + + public void EnableProcessing() + { + // Increase count + processingcount++; + + // If not already enabled, enable processing now + if (!processor.Enabled) + { + processor.Enabled = true; + lastupdatetime = General.stopwatch.ElapsedMilliseconds; + } + } + + public void DisableProcessing() + { + // Increase count + processingcount--; + if (processingcount < 0) processingcount = 0; + + // Turn off + if (processor.Enabled && (processingcount == 0)) + processor.Enabled = false; + } + + internal void StopProcessing() + { + // Turn off + processingcount = 0; + processor.Enabled = false; + } + + // Processor event + private void processor_Tick(object sender, EventArgs e) + { + Vector2D deltamouse; + double curtime = General.stopwatch.ElapsedMilliseconds; + double deltatime = curtime - lastupdatetime; + lastupdatetime = curtime; + + // In exclusive mouse mode? + if (mouseinput != null) + { + // Process mouse input + deltamouse = mouseinput.Process(); + if ((General.Map != null) && (General.Editing.Mode != null)) + General.Editing.Mode.OnMouseInput(deltamouse); + } + + // Process signal + if ((General.Map != null) && (General.Editing.Mode != null)) + General.Editing.Mode.OnProcess(deltatime); + } + + #endregion + + #region ================== Dockers + + // This adds a docker + public void AddDocker(Docker d) + { + // Make sure the full name is set with the plugin name as prefix + Plugin plugin = General.Plugins.FindPluginByAssembly(Assembly.GetCallingAssembly()); + d.MakeFullName(plugin.Name.ToLowerInvariant()); + + dockerspanel.Add(d); + } + + // This removes a docker + public bool RemoveDocker(Docker d) + { + // Make sure the full name is set with the plugin name as prefix + Plugin plugin = General.Plugins.FindPluginByAssembly(Assembly.GetCallingAssembly()); + d.MakeFullName(plugin.Name.ToLowerInvariant()); + + // We must release all keys because the focus may be stolen when + // this was the selected docker (the previous docker is automatically selected) + ReleaseAllKeys(); + + return dockerspanel.Remove(d); + } + + // This selects a docker + public bool SelectDocker(Docker d) + { + // Make sure the full name is set with the plugin name as prefix + Plugin plugin = General.Plugins.FindPluginByAssembly(Assembly.GetCallingAssembly()); + d.MakeFullName(plugin.Name.ToLowerInvariant()); + + // We must release all keys because the focus will be stolen + ReleaseAllKeys(); + + return dockerspanel.SelectDocker(d); + } + + // This selects the previous selected docker + public void SelectPreviousDocker() + { + // We must release all keys because the focus will be stolen + ReleaseAllKeys(); + + dockerspanel.SelectPrevious(); + } + + // Mouse enters dockers window + private void dockerspanel_MouseContainerEnter(object sender, EventArgs e) + { + if (General.Settings.CollapseDockers) + dockerscollapser.Start(); + + dockerspanel.Expand(); + } + + // Automatic collapsing + private void dockerscollapser_Tick(object sender, EventArgs e) + { + if (General.Settings.CollapseDockers) + { + if (!dockerspanel.IsFocused) + { + Point p = this.PointToClient(Cursor.Position); + Rectangle r = new Rectangle(dockerspanel.Location, dockerspanel.Size); + if (!r.IntersectsWith(new Rectangle(p, Size.Empty))) + { + dockerspanel.Collapse(); + dockerscollapser.Stop(); + } + } + } + else + { + dockerscollapser.Stop(); + } + } + + // User resizes the docker + private void dockerspanel_UserResize(object sender, EventArgs e) + { + General.Settings.DockersWidth = dockerspanel.Width; + + if (!General.Settings.CollapseDockers) + dockersspace.Width = dockerspanel.Width; + } + + #endregion + } +} \ No newline at end of file diff --git a/Source/Core/Windows/MainForm.resx b/Source/Core/Windows/MainForm.resx new file mode 100644 index 0000000..752343f --- /dev/null +++ b/Source/Core/Windows/MainForm.resx @@ -0,0 +1,1710 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + True + + + 17, 17 + + + True + + + 127, 17 + + + True + + + 218, 17 + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + 319, 17 + + + 437, 17 + + + 541, 17 + + + 661, 17 + + + True + + + True + + + 781, 17 + + + True + + + + + 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 + + + \ No newline at end of file diff --git a/Source/Core/Windows/MapOptionsForm.Designer.cs b/Source/Core/Windows/MapOptionsForm.Designer.cs new file mode 100644 index 0000000..aa538e8 --- /dev/null +++ b/Source/Core/Windows/MapOptionsForm.Designer.cs @@ -0,0 +1,224 @@ +namespace CodeImp.DoomBuilder.Windows +{ + partial class MapOptionsForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.Windows.Forms.Label label3; + System.Windows.Forms.Label label2; + System.Windows.Forms.Label label1; + System.Windows.Forms.GroupBox panelsettings; + System.Windows.Forms.Label label4; + this.levelname = new System.Windows.Forms.TextBox(); + this.config = new System.Windows.Forms.ComboBox(); + this.apply = new System.Windows.Forms.Button(); + this.cancel = new System.Windows.Forms.Button(); + this.panelres = new System.Windows.Forms.GroupBox(); + this.strictpatches = new System.Windows.Forms.CheckBox(); + this.datalocations = new CodeImp.DoomBuilder.Controls.ResourceListEditor(); + label3 = new System.Windows.Forms.Label(); + label2 = new System.Windows.Forms.Label(); + label1 = new System.Windows.Forms.Label(); + panelsettings = new System.Windows.Forms.GroupBox(); + label4 = new System.Windows.Forms.Label(); + panelsettings.SuspendLayout(); + this.panelres.SuspendLayout(); + this.SuspendLayout(); + // + // label3 + // + label3.AutoSize = true; + label3.Location = new System.Drawing.Point(239, 76); + label3.Name = "label3"; + label3.Size = new System.Drawing.Size(90, 14); + label3.TabIndex = 9; + label3.Text = "example: MAP01"; + // + // label2 + // + label2.AutoSize = true; + label2.Location = new System.Drawing.Point(58, 76); + label2.Name = "label2"; + label2.Size = new System.Drawing.Size(65, 14); + label2.TabIndex = 7; + label2.Text = "Level name:"; + // + // label1 + // + label1.AutoSize = true; + label1.Location = new System.Drawing.Point(18, 34); + label1.Name = "label1"; + label1.Size = new System.Drawing.Size(105, 14); + label1.TabIndex = 5; + label1.Text = "Game Configuration:"; + // + // panelsettings + // + panelsettings.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + panelsettings.Controls.Add(label3); + panelsettings.Controls.Add(this.levelname); + panelsettings.Controls.Add(label2); + panelsettings.Controls.Add(this.config); + panelsettings.Controls.Add(label1); + panelsettings.Location = new System.Drawing.Point(12, 12); + panelsettings.Name = "panelsettings"; + panelsettings.Size = new System.Drawing.Size(397, 118); + panelsettings.TabIndex = 0; + panelsettings.TabStop = false; + panelsettings.Text = " Settings "; + // + // levelname + // + this.levelname.CharacterCasing = System.Windows.Forms.CharacterCasing.Upper; + this.levelname.Location = new System.Drawing.Point(129, 73); + this.levelname.Name = "levelname"; + this.levelname.Size = new System.Drawing.Size(94, 20); + this.levelname.TabIndex = 1; + this.levelname.KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.levelname_KeyPress); + // + // config + // + this.config.DropDownHeight = 206; + this.config.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.config.FormattingEnabled = true; + this.config.IntegralHeight = false; + this.config.Location = new System.Drawing.Point(129, 31); + this.config.Name = "config"; + this.config.Size = new System.Drawing.Size(213, 22); + this.config.TabIndex = 0; + this.config.SelectedIndexChanged += new System.EventHandler(this.config_SelectedIndexChanged); + // + // label4 + // + label4.AutoSize = true; + label4.Location = new System.Drawing.Point(15, 190); + label4.Name = "label4"; + label4.Size = new System.Drawing.Size(312, 28); + label4.TabIndex = 17; + label4.Text = "Drag items to change order (lower items override higher items).\r\nGrayed items are" + + " loaded according to the game configuration."; + // + // apply + // + this.apply.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.apply.Location = new System.Drawing.Point(179, 392); + this.apply.Name = "apply"; + this.apply.Size = new System.Drawing.Size(112, 25); + this.apply.TabIndex = 2; + this.apply.Text = "OK"; + this.apply.UseVisualStyleBackColor = true; + this.apply.Click += new System.EventHandler(this.apply_Click); + // + // cancel + // + this.cancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.cancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.cancel.Location = new System.Drawing.Point(297, 392); + this.cancel.Name = "cancel"; + this.cancel.Size = new System.Drawing.Size(112, 25); + this.cancel.TabIndex = 3; + this.cancel.Text = "Cancel"; + this.cancel.UseVisualStyleBackColor = true; + this.cancel.Click += new System.EventHandler(this.cancel_Click); + // + // panelres + // + this.panelres.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.panelres.Controls.Add(this.strictpatches); + this.panelres.Controls.Add(this.datalocations); + this.panelres.Controls.Add(label4); + this.panelres.Location = new System.Drawing.Point(12, 141); + this.panelres.Name = "panelres"; + this.panelres.Size = new System.Drawing.Size(397, 230); + this.panelres.TabIndex = 1; + this.panelres.TabStop = false; + this.panelres.Text = " Resources "; + // + // strictpatches + // + this.strictpatches.AutoSize = true; + this.strictpatches.Location = new System.Drawing.Point(15, 27); + this.strictpatches.Name = "strictpatches"; + this.strictpatches.Size = new System.Drawing.Size(352, 18); + this.strictpatches.TabIndex = 20; + this.strictpatches.Text = "Strictly load patches between P_START and P_END only for this file"; + this.strictpatches.UseVisualStyleBackColor = true; + // + // datalocations + // + this.datalocations.DialogOffset = new System.Drawing.Point(40, 20); + this.datalocations.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.datalocations.Location = new System.Drawing.Point(15, 57); + this.datalocations.Name = "datalocations"; + this.datalocations.Size = new System.Drawing.Size(368, 130); + this.datalocations.TabIndex = 0; + // + // MapOptionsForm + // + this.AcceptButton = this.apply; + this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; + this.CancelButton = this.cancel; + this.ClientSize = new System.Drawing.Size(421, 429); + this.Controls.Add(this.panelres); + this.Controls.Add(this.cancel); + this.Controls.Add(this.apply); + this.Controls.Add(panelsettings); + this.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "MapOptionsForm"; + this.Opacity = 0; + this.ShowIcon = false; + this.ShowInTaskbar = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Map Options"; + this.HelpRequested += new System.Windows.Forms.HelpEventHandler(this.MapOptionsForm_HelpRequested); + panelsettings.ResumeLayout(false); + panelsettings.PerformLayout(); + this.panelres.ResumeLayout(false); + this.panelres.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.TextBox levelname; + private System.Windows.Forms.ComboBox config; + private System.Windows.Forms.Button apply; + private System.Windows.Forms.Button cancel; + private System.Windows.Forms.GroupBox panelres; + private CodeImp.DoomBuilder.Controls.ResourceListEditor datalocations; + private System.Windows.Forms.CheckBox strictpatches; + + + } +} \ No newline at end of file diff --git a/Source/Core/Windows/MapOptionsForm.cs b/Source/Core/Windows/MapOptionsForm.cs new file mode 100644 index 0000000..e8a43aa --- /dev/null +++ b/Source/Core/Windows/MapOptionsForm.cs @@ -0,0 +1,247 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Text; +using System.Windows.Forms; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Data; +using CodeImp.DoomBuilder.IO; +using System.IO; +using CodeImp.DoomBuilder.Config; +using CodeImp.DoomBuilder.Controls; + +#endregion + +namespace CodeImp.DoomBuilder.Windows +{ + internal partial class MapOptionsForm : DelayedForm + { + // Variables + private MapOptions options; + private bool newmap; + + // Properties + public MapOptions Options { get { return options; } } + public bool IsForNewMap { get { return newmap; } set { newmap = value; } } + + // Constructor + public MapOptionsForm(MapOptions options) + { + int index; + + // Initialize + InitializeComponent(); + + // Keep settings + this.options = options; + + // Go for all configurations + for (int i = 0; i < General.Configs.Count; i++) + { + // Add config name to list + index = config.Items.Add(General.Configs[i]); + + // Is this configuration currently selected? + if (string.Compare(General.Configs[i].Filename, options.ConfigFile, true) == 0) + { + // Select this item + config.SelectedIndex = index; + } + } + + // Set the level name + levelname.Text = options.CurrentName; + + // Set strict patches loading + strictpatches.Checked = options.StrictPatches; + + // Fill the resources list + datalocations.EditResourceLocationList(options.Resources); + } + + // OK clicked + private void apply_Click(object sender, EventArgs e) + { + Configuration newcfg; + WAD sourcewad; + bool conflictingname; + + // Configuration selected? + if (config.SelectedIndex == -1) + { + // Select a configuration! + MessageBox.Show(this, "Please select a game configuration to use for editing your map.", Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Warning); + config.Focus(); + return; + } + + // Level name empty? + if (levelname.Text.Length == 0) + { + // Enter a level name! + MessageBox.Show(this, "Please enter a level name for your map.", Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Warning); + levelname.Focus(); + return; + } + + // Collect information + ConfigurationInfo configinfo = General.Configs[config.SelectedIndex]; + DataLocationList locations = datalocations.GetResources(); + + // When making a new map, check if we should warn the user for missing resources + if (newmap && (locations.Count == 0) && (configinfo.Resources.Count == 0)) + { + if (MessageBox.Show(this, "You are about to make a map without selecting any resources. Textures, flats and " + + "sprites may not be shown correctly or may not show up at all. Do you want to continue?", Application.ProductName, + MessageBoxButtons.YesNo, MessageBoxIcon.Warning, MessageBoxDefaultButton.Button2) == DialogResult.No) + { + return; + } + } + + // Next checks are only for maps that are already opened + if (!newmap) + { + // Now we check if the map name the user has given does already exist in the source WAD file + // We have to warn the user about that, because it would create a level name conflict in the WAD + + // Level name changed and the map exists in a source wad? + if ((levelname.Text != options.CurrentName) && (General.Map != null) && + (General.Map.FilePathName != "") && File.Exists(General.Map.FilePathName)) + { + // Open the source wad file to check for conflicting name + sourcewad = new WAD(General.Map.FilePathName, true); + conflictingname = (sourcewad.FindLumpIndex(levelname.Text) > -1); + sourcewad.Dispose(); + + // Names conflict? + if (conflictingname) + { + // Show warning! + if (General.ShowWarningMessage("The map name \"" + levelname.Text + "\" is already in use by another map or data lump in the source WAD file. Saving your map with this name will cause conflicting data lumps in the WAD file. Do you want to continue?", MessageBoxButtons.YesNo, MessageBoxDefaultButton.Button2) == DialogResult.No) + { + return; + } + } + } + + // When the user changed the configuration to one that has a different read/write interface, + // we have to warn the user that the map may not be compatible. + + // Configuration changed? + if ((options.ConfigFile != "") && (General.Configs[config.SelectedIndex].Filename != options.ConfigFile)) + { + // Load the new cfg file + newcfg = General.LoadGameConfiguration(General.Configs[config.SelectedIndex].Filename); + if (newcfg == null) return; + + // Check if the config uses a different IO interface + if (newcfg.ReadSetting("formatinterface", "") != General.Map.Config.FormatInterface) + { + // Warn the user about IO interface change + if (General.ShowWarningMessage("The game configuration you selected uses a different file format than your current map. Because your map was not designed for this format it may cause the map to work incorrectly in the game. Do you want to continue?", MessageBoxButtons.YesNo, MessageBoxDefaultButton.Button2) == DialogResult.No) + { + // Reset to old configuration + for (int i = 0; i < config.Items.Count; i++) + { + // Is this configuration the old config? + if (string.Compare(General.Configs[i].Filename, options.ConfigFile, true) == 0) + { + // Select this item + config.SelectedIndex = i; + } + } + return; + } + } + } + } + + // Apply changes + options.ClearResources(); + options.ConfigFile = General.Configs[config.SelectedIndex].Filename; + options.CurrentName = levelname.Text.Trim().ToUpper(); + options.StrictPatches = strictpatches.Checked; + options.CopyResources(datalocations.GetResources()); + + // Reset default drawing textures + General.Settings.DefaultTexture = null; + General.Settings.DefaultFloorTexture = null; + General.Settings.DefaultCeilingTexture = null; + + // Hide window + this.DialogResult = DialogResult.OK; + this.Close(); + } + + // Cancel clicked + private void cancel_Click(object sender, EventArgs e) + { + // Just hide window + this.DialogResult = DialogResult.Cancel; + this.Close(); + } + + // Game configuration chosen + private void config_SelectedIndexChanged(object sender, EventArgs e) + { + ConfigurationInfo ci; + + // Anything selected? + if (config.SelectedIndex > -1) + { + // Get the info + ci = (ConfigurationInfo)config.SelectedItem; + + // No lump name in the name field? + if (levelname.Text.Trim().Length == 0) + { + // Get default lump name from configuration + levelname.Text = ci.DefaultLumpName; + } + + // Show resources + datalocations.FixedResourceLocationList(ci.Resources); + } + } + + // When keys are pressed in the level name field + private void levelname_KeyPress(object sender, KeyPressEventArgs e) + { + string allowedchars = Lump.MAP_LUMP_NAME_CHARS + Lump.MAP_LUMP_NAME_CHARS.ToLowerInvariant() + "\b"; + + // Check if key is not allowed + if (allowedchars.IndexOf(e.KeyChar) == -1) + { + // Cancel this + e.Handled = true; + } + } + + // Help + private void MapOptionsForm_HelpRequested(object sender, HelpEventArgs hlpevent) + { + General.ShowHelp("w_mapoptions.html"); + hlpevent.Handled = true; + } + } +} \ No newline at end of file diff --git a/Source/Core/Windows/MapOptionsForm.resx b/Source/Core/Windows/MapOptionsForm.resx new file mode 100644 index 0000000..671f29d --- /dev/null +++ b/Source/Core/Windows/MapOptionsForm.resx @@ -0,0 +1,174 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + True + + + False + + + True + + + False + + + True + + + False + + + True + + + False + + + True + + + True + + + True + + + False + + + True + + + True + + + True + + + True + + + True + + + True + + \ No newline at end of file diff --git a/Source/Core/Windows/MessageBeepType.cs b/Source/Core/Windows/MessageBeepType.cs new file mode 100644 index 0000000..b6b3de7 --- /dev/null +++ b/Source/Core/Windows/MessageBeepType.cs @@ -0,0 +1,50 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using System.Drawing; +using System.ComponentModel; +using CodeImp.DoomBuilder.Map; +using SlimDX.Direct3D9; +using SlimDX; +using CodeImp.DoomBuilder.Geometry; +using System.Drawing.Imaging; +using CodeImp.DoomBuilder.Data; +using CodeImp.DoomBuilder.Editing; + +#endregion + +namespace CodeImp.DoomBuilder.Windows +{ + public enum MessageBeepType : int + { + Default = -1, + Ok = 0x00000000, + Error = 0x00000010, + Question = 0x00000020, + Warning = 0x00000030, + Information = 0x00000040, + } +} diff --git a/Source/Core/Windows/OpenMapOptionsForm.Designer.cs b/Source/Core/Windows/OpenMapOptionsForm.Designer.cs new file mode 100644 index 0000000..005c5e7 --- /dev/null +++ b/Source/Core/Windows/OpenMapOptionsForm.Designer.cs @@ -0,0 +1,216 @@ +namespace CodeImp.DoomBuilder.Windows +{ + partial class OpenMapOptionsForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.Windows.Forms.ColumnHeader columnHeader1; + System.Windows.Forms.Label label1; + System.Windows.Forms.Label label2; + System.Windows.Forms.Label label3; + this.panelres = new System.Windows.Forms.GroupBox(); + this.strictpatches = new System.Windows.Forms.CheckBox(); + this.datalocations = new CodeImp.DoomBuilder.Controls.ResourceListEditor(); + this.apply = new System.Windows.Forms.Button(); + this.cancel = new System.Windows.Forms.Button(); + this.config = new System.Windows.Forms.ComboBox(); + this.mapslist = new System.Windows.Forms.ListView(); + columnHeader1 = new System.Windows.Forms.ColumnHeader(); + label1 = new System.Windows.Forms.Label(); + label2 = new System.Windows.Forms.Label(); + label3 = new System.Windows.Forms.Label(); + this.panelres.SuspendLayout(); + this.SuspendLayout(); + // + // columnHeader1 + // + columnHeader1.Text = "Map name"; + // + // label1 + // + label1.AutoSize = true; + label1.Location = new System.Drawing.Point(30, 24); + label1.Name = "label1"; + label1.Size = new System.Drawing.Size(105, 14); + label1.TabIndex = 14; + label1.Text = "Game Configuration:"; + // + // label2 + // + label2.Location = new System.Drawing.Point(12, 57); + label2.Name = "label2"; + label2.Size = new System.Drawing.Size(396, 30); + label2.TabIndex = 16; + label2.Text = "With the above selected configuration, the maps shown below were found in the cho" + + "sen WAD file. Please select the map to load for editing."; + // + // label3 + // + label3.AutoSize = true; + label3.Location = new System.Drawing.Point(14, 193); + label3.Name = "label3"; + label3.Size = new System.Drawing.Size(312, 28); + label3.TabIndex = 17; + label3.Text = "Drag items to change order (lower items override higher items).\r\nGrayed items are" + + " loaded according to the game configuration."; + // + // panelres + // + this.panelres.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.panelres.Controls.Add(this.strictpatches); + this.panelres.Controls.Add(this.datalocations); + this.panelres.Controls.Add(label3); + this.panelres.Location = new System.Drawing.Point(12, 215); + this.panelres.Name = "panelres"; + this.panelres.Size = new System.Drawing.Size(396, 231); + this.panelres.TabIndex = 2; + this.panelres.TabStop = false; + this.panelres.Text = " Resources "; + // + // strictpatches + // + this.strictpatches.AutoSize = true; + this.strictpatches.Location = new System.Drawing.Point(14, 27); + this.strictpatches.Name = "strictpatches"; + this.strictpatches.Size = new System.Drawing.Size(352, 18); + this.strictpatches.TabIndex = 19; + this.strictpatches.Text = "Strictly load patches between P_START and P_END only for this file"; + this.strictpatches.UseVisualStyleBackColor = true; + // + // datalocations + // + this.datalocations.DialogOffset = new System.Drawing.Point(40, 20); + this.datalocations.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.datalocations.Location = new System.Drawing.Point(14, 58); + this.datalocations.Name = "datalocations"; + this.datalocations.Size = new System.Drawing.Size(368, 127); + this.datalocations.TabIndex = 0; + // + // apply + // + this.apply.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.apply.Location = new System.Drawing.Point(178, 462); + this.apply.Name = "apply"; + this.apply.Size = new System.Drawing.Size(112, 25); + this.apply.TabIndex = 3; + this.apply.Text = "OK"; + this.apply.UseVisualStyleBackColor = true; + this.apply.Click += new System.EventHandler(this.apply_Click); + // + // cancel + // + this.cancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.cancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.cancel.Location = new System.Drawing.Point(296, 462); + this.cancel.Name = "cancel"; + this.cancel.Size = new System.Drawing.Size(112, 25); + this.cancel.TabIndex = 4; + this.cancel.Text = "Cancel"; + this.cancel.UseVisualStyleBackColor = true; + this.cancel.Click += new System.EventHandler(this.cancel_Click); + // + // config + // + this.config.DropDownHeight = 206; + this.config.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.config.FormattingEnabled = true; + this.config.IntegralHeight = false; + this.config.Location = new System.Drawing.Point(141, 21); + this.config.Name = "config"; + this.config.Size = new System.Drawing.Size(242, 22); + this.config.TabIndex = 0; + this.config.SelectedIndexChanged += new System.EventHandler(this.config_SelectedIndexChanged); + // + // mapslist + // + this.mapslist.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.mapslist.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + columnHeader1}); + this.mapslist.FullRowSelect = true; + this.mapslist.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.None; + this.mapslist.HideSelection = false; + this.mapslist.LabelWrap = false; + this.mapslist.Location = new System.Drawing.Point(12, 90); + this.mapslist.MultiSelect = false; + this.mapslist.Name = "mapslist"; + this.mapslist.ShowGroups = false; + this.mapslist.Size = new System.Drawing.Size(396, 110); + this.mapslist.Sorting = System.Windows.Forms.SortOrder.Ascending; + this.mapslist.TabIndex = 1; + this.mapslist.UseCompatibleStateImageBehavior = false; + this.mapslist.View = System.Windows.Forms.View.List; + this.mapslist.DoubleClick += new System.EventHandler(this.mapslist_DoubleClick); + this.mapslist.ItemSelectionChanged += new System.Windows.Forms.ListViewItemSelectionChangedEventHandler(this.mapslist_ItemSelectionChanged); + // + // OpenMapOptionsForm + // + this.AcceptButton = this.apply; + this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; + this.CancelButton = this.cancel; + this.ClientSize = new System.Drawing.Size(420, 499); + this.Controls.Add(this.mapslist); + this.Controls.Add(label2); + this.Controls.Add(this.config); + this.Controls.Add(label1); + this.Controls.Add(this.cancel); + this.Controls.Add(this.apply); + this.Controls.Add(this.panelres); + this.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "OpenMapOptionsForm"; + this.Opacity = 0; + this.ShowIcon = false; + this.ShowInTaskbar = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Open Map Options"; + this.Shown += new System.EventHandler(this.OpenMapOptionsForm_Shown); + this.HelpRequested += new System.Windows.Forms.HelpEventHandler(this.OpenMapOptionsForm_HelpRequested); + this.panelres.ResumeLayout(false); + this.panelres.PerformLayout(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Button apply; + private System.Windows.Forms.Button cancel; + private System.Windows.Forms.ComboBox config; + private System.Windows.Forms.GroupBox panelres; + private System.Windows.Forms.ListView mapslist; + private CodeImp.DoomBuilder.Controls.ResourceListEditor datalocations; + private System.Windows.Forms.CheckBox strictpatches; + + + } +} \ No newline at end of file diff --git a/Source/Core/Windows/OpenMapOptionsForm.cs b/Source/Core/Windows/OpenMapOptionsForm.cs new file mode 100644 index 0000000..13889b3 --- /dev/null +++ b/Source/Core/Windows/OpenMapOptionsForm.cs @@ -0,0 +1,425 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Text; +using System.Windows.Forms; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Map; +using System.IO; +using System.Collections; +using System.Diagnostics; +using CodeImp.DoomBuilder.Data; +using CodeImp.DoomBuilder.Config; +using CodeImp.DoomBuilder.Controls; + +#endregion + +namespace CodeImp.DoomBuilder.Windows +{ + internal partial class OpenMapOptionsForm : DelayedForm + { + // Variables + private Configuration mapsettings; + private MapOptions options; + private WAD wadfile; + private string filepathname; + private string selectedmapname; + + // Properties + public string FilePathName { get { return filepathname; } } + public MapOptions Options { get { return options; } } + + // Constructor + public OpenMapOptionsForm(string filepathname) + { + // Initialize + InitializeComponent(); + this.Text = "Open Map from " + Path.GetFileName(filepathname); + this.filepathname = filepathname; + this.options = new MapOptions(); + } + + // This loads the settings and attempt to find a suitable config + private void LoadSettings() + { + string dbsfile; + string gameconfig; + int index; + + // Busy + Cursor.Current = Cursors.WaitCursor; + + // Check if the file exists + if (!File.Exists(filepathname)) + { + // WAD file does not exist + MessageBox.Show(this, "Could not open the WAD file: The file does not exist.", Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error); + this.DialogResult = DialogResult.Cancel; + this.Close(); + return; + } + + try + { + // Open the WAD file + wadfile = new WAD(filepathname, true); + } + catch (Exception) + { + // Unable to open WAD file (or its config) + MessageBox.Show(this, "Could not open the WAD file for reading. Please make sure the file you selected is valid and is not in use by any other application.", Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error); + if (wadfile != null) wadfile.Dispose(); + this.DialogResult = DialogResult.Cancel; + this.Close(); + return; + } + + // Open the Map Settings configuration + dbsfile = filepathname.Substring(0, filepathname.Length - 4) + ".dbs"; + if (File.Exists(dbsfile)) + try { mapsettings = new Configuration(dbsfile, true); } + catch (Exception) { mapsettings = new Configuration(true); } + else + mapsettings = new Configuration(true); + + // Check strict patches box + strictpatches.Checked = mapsettings.ReadSetting("strictpatches", false); + + // Check what game configuration is preferred + gameconfig = mapsettings.ReadSetting("gameconfig", ""); + + // Go for all configurations + for (int i = 0; i < General.Configs.Count; i++) + { + // Add config name to list + index = config.Items.Add(General.Configs[i]); + + // This is the preferred game configuration? + if (General.Configs[i].Filename == gameconfig) + { + // Select this item + config.SelectedIndex = index; + } + } + + // Still no configuration selected? + if (config.SelectedIndex == -1) + { + // Then go for all configurations to find a suitable one + for (int i = 0; i < General.Configs.Count; i++) + { + // Check if a resource location is set for this configuration + if (General.Configs[i].Resources.Count > 0) + { + // Match the wad against this configuration + if (MatchConfiguration(General.Configs[i].Filename, wadfile)) + { + // Select this item + config.SelectedIndex = i; + break; + } + } + } + } + + // Done + Cursor.Current = Cursors.Default; + } + + // This matches a WAD file with the specified game configuration + // by checking if the specific lumps are detected + private bool MatchConfiguration(string configfile, WAD wadfile) + { + Configuration cfg; + IDictionary detectlumps; + Lump lumpresult; + bool result = false; + + // Load the configuration + cfg = General.LoadGameConfiguration(configfile); + + // Get the lumps to detect + detectlumps = cfg.ReadSetting("gamedetect", new Hashtable()); + + // Go for all the lumps + foreach (DictionaryEntry lmp in detectlumps) + { + // Setting not broken? + if ((lmp.Value is int) && (lmp.Key is string)) + { + // Find the lump in the WAD file + lumpresult = wadfile.FindLump((string)lmp.Key); + + // If one of these lumps must exist, and it is found + if (((int)lmp.Value == 1) && (lumpresult != null)) + { + // Good result. + result = true; + } + // If this lumps may not exist, and it is found + else if (((int)lmp.Value == 2) && (lumpresult != null)) + { + // Bad result. + result = false; + break; + } + // If this lumps must exist, and is found + else if (((int)lmp.Value == 3) && (lumpresult != null)) + { + // Good result. + result = true; + } + // If this lumps must exist, and it is missing + else if (((int)lmp.Value == 3) && (lumpresult == null)) + { + // Bad result. + result = false; + break; + } + } + } + + // Return result + return result; + } + + // Configuration is selected + private void config_SelectedIndexChanged(object sender, EventArgs e) + { + List mapnames; + ConfigurationInfo ci; + Configuration cfg; + IDictionary maplumpnames; + int scanindex, checkoffset; + int lumpsfound, lumpsrequired = 0; + string lumpname, selectedname = ""; + + // Anything selected? + if (config.SelectedIndex > -1) + { + // Keep selected name, if any + if (mapslist.SelectedItems.Count > 0) + selectedname = mapslist.SelectedItems[0].Text; + + // Make an array for the map names + mapnames = new List(); + + // Get selected configuration info + ci = (ConfigurationInfo)config.SelectedItem; + + // Load this configuration + cfg = General.LoadGameConfiguration(ci.Filename); + + // Get the map lump names + maplumpnames = cfg.ReadSetting("maplumpnames", new Hashtable()); + + // Count how many required lumps we have to find + foreach (DictionaryEntry ml in maplumpnames) + { + // Ignore the map header (it will not be found because the name is different) + if (ml.Key.ToString() != MapManager.CONFIG_MAP_HEADER) + { + // Read lump setting and count it + if (cfg.ReadSetting("maplumpnames." + ml.Key + ".required", false)) lumpsrequired++; + } + } + + // Go for all the lumps in the wad + for (scanindex = 0; scanindex < (wadfile.Lumps.Count - 1); scanindex++) + { + // Make sure this lump is not part of the map + if (!maplumpnames.Contains(wadfile.Lumps[scanindex].Name)) + { + // Reset check + lumpsfound = 0; + checkoffset = 1; + + // Continue while still within bounds and lumps are still recognized + while (((scanindex + checkoffset) < wadfile.Lumps.Count) && + maplumpnames.Contains(wadfile.Lumps[scanindex + checkoffset].Name)) + { + // Count the lump when it is marked as required + lumpname = wadfile.Lumps[scanindex + checkoffset].Name; + if (cfg.ReadSetting("maplumpnames." + lumpname + ".required", false)) lumpsfound++; + + // Check the next lump + checkoffset++; + } + + // Map found? Then add it to the list + if (lumpsfound >= lumpsrequired) + mapnames.Add(new ListViewItem(wadfile.Lumps[scanindex].Name)); + } + } + + // Clear the list and add the new map names + mapslist.BeginUpdate(); + mapslist.Items.Clear(); + mapslist.Items.AddRange(mapnames.ToArray()); + mapslist.Sort(); + + // Go for all items in the list + foreach (ListViewItem item in mapslist.Items) + { + // Was this item previously selected? + if (item.Text == selectedname) + { + // Select it again + item.Selected = true; + break; + } + } + if ((mapslist.SelectedItems.Count == 0) && (mapslist.Items.Count > 0)) mapslist.Items[0].Selected = true; + mapslist.EndUpdate(); + + // Show configuration resources + datalocations.FixedResourceLocationList(ci.Resources); + } + } + + // OK clicked + private void apply_Click(object sender, EventArgs e) + { + // Configuration selected? + if (config.SelectedIndex == -1) + { + // Select a configuration! + MessageBox.Show(this, "Please select a game configuration to use for editing your map.", Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Warning); + config.Focus(); + return; + } + + // Collect information + ConfigurationInfo configinfo = General.Configs[config.SelectedIndex]; + DataLocationList locations = datalocations.GetResources(); + + // No map selected? + if (mapslist.SelectedItems.Count == 0) + { + // Choose a map! + MessageBox.Show(this, "Please select a map to load for editing.", Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Warning); + mapslist.Focus(); + return; + } + + // Check if we should warn the user for missing resources + if ((wadfile.Type != WAD.TYPE_IWAD) && (locations.Count == 0) && (configinfo.Resources.Count == 0)) + { + if (MessageBox.Show(this, "You are about to load a map without selecting any resources. Textures, flats and " + + "sprites may not be shown correctly or may not show up at all. Do you want to continue?", Application.ProductName, + MessageBoxButtons.YesNo, MessageBoxIcon.Warning, MessageBoxDefaultButton.Button2) == DialogResult.No) + { + return; + } + } + + // Apply changes + options.ClearResources(); + options.ConfigFile = configinfo.Filename; + options.CurrentName = mapslist.SelectedItems[0].Text; + options.StrictPatches = strictpatches.Checked; + options.CopyResources(locations); + + // Hide window + wadfile.Dispose(); + this.DialogResult = DialogResult.OK; + this.Close(); + } + + // Cancel clicked + private void cancel_Click(object sender, EventArgs e) + { + // Just hide window + wadfile.Dispose(); + this.DialogResult = DialogResult.Cancel; + this.Close(); + } + + // Window is shown + private void OpenMapOptionsForm_Shown(object sender, EventArgs e) + { + // Update window + this.Update(); + + // Load settings + LoadSettings(); + } + + // Map name doubleclicked + private void mapslist_DoubleClick(object sender, EventArgs e) + { + // Click OK + if (mapslist.SelectedItems.Count > 0) apply.PerformClick(); + } + + // Map name selected + private void mapslist_ItemSelectionChanged(object sender, ListViewItemSelectionChangedEventArgs e) + { + DataLocationList locations; + DataLocationList listedlocations; + + // Map previously selected? + if ((selectedmapname != null) && (selectedmapname != "")) + { + // Get locations from previous selected map settings + locations = new DataLocationList(mapsettings, "maps." + selectedmapname + ".resources"); + listedlocations = datalocations.GetResources(); + + // Remove data locations that this map has in its config + foreach (DataLocation dl in locations) + listedlocations.Remove(dl); + + // Set new data locations + datalocations.EditResourceLocationList(listedlocations); + + // Done + selectedmapname = null; + } + + // Anything selected? + if (mapslist.SelectedItems.Count > 0) + { + // Get the map name + selectedmapname = mapslist.SelectedItems[0].Text; + options = new MapOptions(mapsettings, selectedmapname); + + // Get locations from previous selected map settings + locations = new DataLocationList(mapsettings, "maps." + selectedmapname + ".resources"); + listedlocations = datalocations.GetResources(); + + // Add data locations that this map has in its config + foreach (DataLocation dl in locations) + if (!listedlocations.Contains(dl)) listedlocations.Add(dl); + + // Set new data locations + datalocations.EditResourceLocationList(listedlocations); + } + } + + // Help + private void OpenMapOptionsForm_HelpRequested(object sender, HelpEventArgs hlpevent) + { + General.ShowHelp("w_openmapoptions.html"); + hlpevent.Handled = true; + } + } +} \ No newline at end of file diff --git a/Source/Core/Windows/OpenMapOptionsForm.resx b/Source/Core/Windows/OpenMapOptionsForm.resx new file mode 100644 index 0000000..e6a997f --- /dev/null +++ b/Source/Core/Windows/OpenMapOptionsForm.resx @@ -0,0 +1,165 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + False + + + True + + + False + + + True + + + False + + + True + + + False + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + \ No newline at end of file diff --git a/Source/Core/Windows/PasteOptionsForm.Designer.cs b/Source/Core/Windows/PasteOptionsForm.Designer.cs new file mode 100644 index 0000000..b535718 --- /dev/null +++ b/Source/Core/Windows/PasteOptionsForm.Designer.cs @@ -0,0 +1,99 @@ +namespace CodeImp.DoomBuilder.Windows +{ + partial class PasteOptionsForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.paste = new System.Windows.Forms.Button(); + this.cancel = new System.Windows.Forms.Button(); + this.pasteoptions = new CodeImp.DoomBuilder.Controls.PasteOptionsControl(); + this.SuspendLayout(); + // + // paste + // + this.paste.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.paste.Location = new System.Drawing.Point(272, 278); + this.paste.Name = "paste"; + this.paste.Size = new System.Drawing.Size(112, 25); + this.paste.TabIndex = 3; + this.paste.Text = "Paste"; + this.paste.UseVisualStyleBackColor = true; + this.paste.Click += new System.EventHandler(this.paste_Click); + // + // cancel + // + this.cancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.cancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.cancel.Location = new System.Drawing.Point(390, 278); + this.cancel.Name = "cancel"; + this.cancel.Size = new System.Drawing.Size(112, 25); + this.cancel.TabIndex = 4; + this.cancel.Text = "Cancel"; + this.cancel.UseVisualStyleBackColor = true; + this.cancel.Click += new System.EventHandler(this.cancel_Click); + // + // pasteoptions + // + this.pasteoptions.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.pasteoptions.Location = new System.Drawing.Point(12, 12); + this.pasteoptions.Name = "pasteoptions"; + this.pasteoptions.Size = new System.Drawing.Size(490, 260); + this.pasteoptions.TabIndex = 5; + // + // PasteOptionsForm + // + this.AcceptButton = this.paste; + this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; + this.CancelButton = this.cancel; + this.ClientSize = new System.Drawing.Size(514, 318); + this.ControlBox = false; + this.Controls.Add(this.pasteoptions); + this.Controls.Add(this.cancel); + this.Controls.Add(this.paste); + this.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "PasteOptionsForm"; + this.ShowIcon = false; + this.ShowInTaskbar = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Paste Special"; + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.Button paste; + private System.Windows.Forms.Button cancel; + private CodeImp.DoomBuilder.Controls.PasteOptionsControl pasteoptions; + } +} \ No newline at end of file diff --git a/Source/Core/Windows/PasteOptionsForm.cs b/Source/Core/Windows/PasteOptionsForm.cs new file mode 100644 index 0000000..79c8890 --- /dev/null +++ b/Source/Core/Windows/PasteOptionsForm.cs @@ -0,0 +1,85 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Text; +using System.Windows.Forms; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Map; +using System.IO; +using System.Collections; +using System.Diagnostics; +using CodeImp.DoomBuilder.Data; +using CodeImp.DoomBuilder.Config; +using CodeImp.DoomBuilder.Controls; + +#endregion + +namespace CodeImp.DoomBuilder.Windows +{ + internal partial class PasteOptionsForm : DelayedForm + { + #region ================== Variables + + private PasteOptions options; + + #endregion + + #region ================== Properties + + public PasteOptions Options { get { return options; } } + + #endregion + + #region ================== Constructor + + // Constructor + public PasteOptionsForm() + { + InitializeComponent(); + + // Get defaults + options = General.Settings.PasteOptions.Copy(); + pasteoptions.Setup(options); + } + + #endregion + + #region ================== Events + + // Paste clicked + private void paste_Click(object sender, EventArgs e) + { + options = pasteoptions.GetOptions(); + this.DialogResult = DialogResult.OK; + this.Close(); + } + + // Cancel clicked + private void cancel_Click(object sender, EventArgs e) + { + this.DialogResult = DialogResult.Cancel; + this.Close(); + } + + #endregion + } +} diff --git a/Source/Core/Windows/PasteOptionsForm.resx b/Source/Core/Windows/PasteOptionsForm.resx new file mode 100644 index 0000000..e8ae763 --- /dev/null +++ b/Source/Core/Windows/PasteOptionsForm.resx @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + True + + + True + + + True + + + True + + \ No newline at end of file diff --git a/Source/Core/Windows/PreferencesController.cs b/Source/Core/Windows/PreferencesController.cs new file mode 100644 index 0000000..b2dc0ce --- /dev/null +++ b/Source/Core/Windows/PreferencesController.cs @@ -0,0 +1,109 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using System.Drawing; +using System.ComponentModel; +using CodeImp.DoomBuilder.Map; +using SlimDX.Direct3D9; +using SlimDX; +using CodeImp.DoomBuilder.Geometry; +using System.Drawing.Imaging; +using CodeImp.DoomBuilder.Data; +using CodeImp.DoomBuilder.Editing; + +#endregion + +namespace CodeImp.DoomBuilder.Windows +{ + public class PreferencesController + { + #region ================== Delegates + + public delegate void AcceptDelegate(PreferencesController controller); + public delegate void CancelDelegate(PreferencesController controller); + + public event AcceptDelegate OnAccept; + public event CancelDelegate OnCancel; + + #endregion + + #region ================== Variables + + private PreferencesForm form; + private bool allowaddtab; + + #endregion + + #region ================== Properties + + internal bool AllowAddTab { get { return allowaddtab; } set { allowaddtab = value; } } + + #endregion + + #region ================== Constructor / Destructor + + // Constructor + internal PreferencesController(PreferencesForm form) + { + // Initialize + this.form = form; + } + + // Destructor + ~PreferencesController() + { + form = null; + OnAccept = null; + OnCancel = null; + } + + #endregion + + #region ================== Methods + + // This adds a preferences tab + public void AddTab(TabPage tab) + { + if (!allowaddtab) throw new InvalidOperationException("Tab pages can only be added when the dialog is being initialized"); + + form.AddTabPage(tab); + } + + // This raises the OnAccept event + public void RaiseAccept() + { + if (OnAccept != null) OnAccept(this); + } + + // This raises the OnCancel event + public void RaiseCancel() + { + if (OnCancel != null) OnCancel(this); + } + + #endregion + } +} diff --git a/Source/Core/Windows/PreferencesForm.Designer.cs b/Source/Core/Windows/PreferencesForm.Designer.cs new file mode 100644 index 0000000..5298e5b --- /dev/null +++ b/Source/Core/Windows/PreferencesForm.Designer.cs @@ -0,0 +1,1686 @@ +namespace CodeImp.DoomBuilder.Windows +{ + partial class PreferencesForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + controller = null; + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.Windows.Forms.Label label7; + System.Windows.Forms.Label label6; + System.Windows.Forms.Label label5; + System.Windows.Forms.GroupBox groupBox1; + System.Windows.Forms.Label label1; + this.scriptontop = new System.Windows.Forms.CheckBox(); + this.zoomfactor = new CodeImp.DoomBuilder.Controls.TransparentTrackBar(); + this.zoomfactorlabel = new System.Windows.Forms.Label(); + this.label19 = new System.Windows.Forms.Label(); + this.autoscrollspeed = new CodeImp.DoomBuilder.Controls.TransparentTrackBar(); + this.autoscrollspeedlabel = new System.Windows.Forms.Label(); + this.label15 = new System.Windows.Forms.Label(); + this.previewsize = new CodeImp.DoomBuilder.Controls.TransparentTrackBar(); + this.previewsizelabel = new System.Windows.Forms.Label(); + this.label12 = new System.Windows.Forms.Label(); + this.label14 = new System.Windows.Forms.Label(); + this.defaultviewmode = new System.Windows.Forms.ComboBox(); + this.keyusedlabel = new System.Windows.Forms.Label(); + this.colorsgroup1 = new System.Windows.Forms.GroupBox(); + this.colorTagonly = new CodeImp.DoomBuilder.Controls.ColorControl(); + this.colorSecret = new CodeImp.DoomBuilder.Controls.ColorControl(); + this.colorMblock = new CodeImp.DoomBuilder.Controls.ColorControl(); + this.coloinvisiblelinedef = new CodeImp.DoomBuilder.Controls.ColorControl(); + this.colorgrid64 = new CodeImp.DoomBuilder.Controls.ColorControl(); + this.colorgrid = new CodeImp.DoomBuilder.Controls.ColorControl(); + this.colorindication = new CodeImp.DoomBuilder.Controls.ColorControl(); + this.colorsoundlinedefs = new CodeImp.DoomBuilder.Controls.ColorControl(); + this.colorspeciallinedefs = new CodeImp.DoomBuilder.Controls.ColorControl(); + this.colorbackcolor = new CodeImp.DoomBuilder.Controls.ColorControl(); + this.colorselection = new CodeImp.DoomBuilder.Controls.ColorControl(); + this.colorvertices = new CodeImp.DoomBuilder.Controls.ColorControl(); + this.colorhighlight = new CodeImp.DoomBuilder.Controls.ColorControl(); + this.colorlinedefs = new CodeImp.DoomBuilder.Controls.ColorControl(); + this.label2 = new System.Windows.Forms.Label(); + this.squarethings = new System.Windows.Forms.CheckBox(); + this.doublesidedalphalabel = new System.Windows.Forms.Label(); + this.qualitydisplay = new System.Windows.Forms.CheckBox(); + this.doublesidedalpha = new CodeImp.DoomBuilder.Controls.TransparentTrackBar(); + this.cancel = new System.Windows.Forms.Button(); + this.apply = new System.Windows.Forms.Button(); + this.tabs = new System.Windows.Forms.TabControl(); + this.tabinterface = new System.Windows.Forms.TabPage(); + this.groupBox5 = new System.Windows.Forms.GroupBox(); + this.toolbar_file = new System.Windows.Forms.CheckBox(); + this.toolbar_testing = new System.Windows.Forms.CheckBox(); + this.toolbar_geometry = new System.Windows.Forms.CheckBox(); + this.toolbar_viewmodes = new System.Windows.Forms.CheckBox(); + this.toolbar_filter = new System.Windows.Forms.CheckBox(); + this.toolbar_prefabs = new System.Windows.Forms.CheckBox(); + this.toolbar_copy = new System.Windows.Forms.CheckBox(); + this.toolbar_undo = new System.Windows.Forms.CheckBox(); + this.toolbar_script = new System.Windows.Forms.CheckBox(); + this.groupBox4 = new System.Windows.Forms.GroupBox(); + this.collapsedockers = new System.Windows.Forms.CheckBox(); + this.dockersposition = new System.Windows.Forms.ComboBox(); + this.label17 = new System.Windows.Forms.Label(); + this.groupBox2 = new System.Windows.Forms.GroupBox(); + this.lightintensitylabel = new System.Windows.Forms.Label(); + this.lightintensity = new CodeImp.DoomBuilder.Controls.TransparentTrackBar(); + this.label18 = new System.Windows.Forms.Label(); + this.viewdistance = new CodeImp.DoomBuilder.Controls.TransparentTrackBar(); + this.movespeed = new CodeImp.DoomBuilder.Controls.TransparentTrackBar(); + this.mousespeed = new CodeImp.DoomBuilder.Controls.TransparentTrackBar(); + this.fieldofview = new CodeImp.DoomBuilder.Controls.TransparentTrackBar(); + this.viewdistancelabel = new System.Windows.Forms.Label(); + this.label13 = new System.Windows.Forms.Label(); + this.invertyaxis = new System.Windows.Forms.CheckBox(); + this.movespeedlabel = new System.Windows.Forms.Label(); + this.label11 = new System.Windows.Forms.Label(); + this.mousespeedlabel = new System.Windows.Forms.Label(); + this.label9 = new System.Windows.Forms.Label(); + this.fieldofviewlabel = new System.Windows.Forms.Label(); + this.label4 = new System.Windows.Forms.Label(); + this.tabkeys = new System.Windows.Forms.TabPage(); + this.listactions = new System.Windows.Forms.ListView(); + this.columncontrolaction = new System.Windows.Forms.ColumnHeader(); + this.columncontrolkey = new System.Windows.Forms.ColumnHeader(); + this.actioncontrolpanel = new System.Windows.Forms.GroupBox(); + this.keyusedlist = new System.Windows.Forms.ListBox(); + this.disregardshiftlabel = new System.Windows.Forms.Label(); + this.actioncontrol = new System.Windows.Forms.ComboBox(); + this.actiontitle = new System.Windows.Forms.Label(); + this.actioncontrolclear = new System.Windows.Forms.Button(); + this.actionkey = new System.Windows.Forms.TextBox(); + this.actiondescription = new System.Windows.Forms.Label(); + this.tabcolors = new System.Windows.Forms.TabPage(); + this.appearancegroup1 = new System.Windows.Forms.GroupBox(); + this.animatevisualselection = new System.Windows.Forms.CheckBox(); + this.blackbrowsers = new System.Windows.Forms.CheckBox(); + this.visualbilinear = new System.Windows.Forms.CheckBox(); + this.classicbilinear = new System.Windows.Forms.CheckBox(); + this.imagebrightnesslabel = new System.Windows.Forms.Label(); + this.imagebrightness = new CodeImp.DoomBuilder.Controls.TransparentTrackBar(); + this.colorsgroup3 = new System.Windows.Forms.GroupBox(); + this.scripttabwidth = new CodeImp.DoomBuilder.Controls.ButtonsNumericTextbox(); + this.scriptautoindent = new System.Windows.Forms.CheckBox(); + this.label10 = new System.Windows.Forms.Label(); + this.panel1 = new System.Windows.Forms.Panel(); + this.scriptfontlabel = new System.Windows.Forms.Label(); + this.scriptfontsize = new System.Windows.Forms.ComboBox(); + this.label8 = new System.Windows.Forms.Label(); + this.scriptfontbold = new System.Windows.Forms.CheckBox(); + this.scriptfontname = new System.Windows.Forms.ComboBox(); + this.label3 = new System.Windows.Forms.Label(); + this.colorconstants = new CodeImp.DoomBuilder.Controls.ColorControl(); + this.colorliterals = new CodeImp.DoomBuilder.Controls.ColorControl(); + this.colorscriptbackground = new CodeImp.DoomBuilder.Controls.ColorControl(); + this.colorkeywords = new CodeImp.DoomBuilder.Controls.ColorControl(); + this.colorlinenumbers = new CodeImp.DoomBuilder.Controls.ColorControl(); + this.colorcomments = new CodeImp.DoomBuilder.Controls.ColorControl(); + this.colorplaintext = new CodeImp.DoomBuilder.Controls.ColorControl(); + this.tabpasting = new System.Windows.Forms.TabPage(); + this.label16 = new System.Windows.Forms.Label(); + this.pasteoptions = new CodeImp.DoomBuilder.Controls.PasteOptionsControl(); + label7 = new System.Windows.Forms.Label(); + label6 = new System.Windows.Forms.Label(); + label5 = new System.Windows.Forms.Label(); + groupBox1 = new System.Windows.Forms.GroupBox(); + label1 = new System.Windows.Forms.Label(); + groupBox1.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.zoomfactor)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.autoscrollspeed)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.previewsize)).BeginInit(); + this.colorsgroup1.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.doublesidedalpha)).BeginInit(); + this.tabs.SuspendLayout(); + this.tabinterface.SuspendLayout(); + this.groupBox5.SuspendLayout(); + this.groupBox4.SuspendLayout(); + this.groupBox2.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.lightintensity)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.viewdistance)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.movespeed)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.mousespeed)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.fieldofview)).BeginInit(); + this.tabkeys.SuspendLayout(); + this.actioncontrolpanel.SuspendLayout(); + this.tabcolors.SuspendLayout(); + this.appearancegroup1.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.imagebrightness)).BeginInit(); + this.colorsgroup3.SuspendLayout(); + this.panel1.SuspendLayout(); + this.tabpasting.SuspendLayout(); + this.SuspendLayout(); + // + // label7 + // + label7.AutoSize = true; + label7.Location = new System.Drawing.Point(20, 172); + label7.Name = "label7"; + label7.Size = new System.Drawing.Size(187, 14); + label7.TabIndex = 7; + label7.Text = "Or select a special input control here:"; + // + // label6 + // + label6.AutoSize = true; + label6.Location = new System.Drawing.Point(20, 30); + label6.Name = "label6"; + label6.Size = new System.Drawing.Size(41, 14); + label6.TabIndex = 2; + label6.Text = "Action:"; + // + // label5 + // + label5.AutoSize = true; + label5.Location = new System.Drawing.Point(20, 122); + label5.Name = "label5"; + label5.Size = new System.Drawing.Size(200, 14); + label5.TabIndex = 4; + label5.Text = "Press the desired key combination here:"; + // + // groupBox1 + // + groupBox1.Controls.Add(this.scriptontop); + groupBox1.Controls.Add(this.zoomfactor); + groupBox1.Controls.Add(this.zoomfactorlabel); + groupBox1.Controls.Add(this.label19); + groupBox1.Controls.Add(this.autoscrollspeed); + groupBox1.Controls.Add(this.autoscrollspeedlabel); + groupBox1.Controls.Add(this.label15); + groupBox1.Controls.Add(this.previewsize); + groupBox1.Controls.Add(this.previewsizelabel); + groupBox1.Controls.Add(this.label12); + groupBox1.Controls.Add(this.label14); + groupBox1.Controls.Add(this.defaultviewmode); + groupBox1.Location = new System.Drawing.Point(8, 8); + groupBox1.Name = "groupBox1"; + groupBox1.Size = new System.Drawing.Size(331, 286); + groupBox1.TabIndex = 0; + groupBox1.TabStop = false; + groupBox1.Text = " Options "; + // + // scriptontop + // + this.scriptontop.AutoSize = true; + this.scriptontop.Location = new System.Drawing.Point(32, 248); + this.scriptontop.Name = "scriptontop"; + this.scriptontop.Size = new System.Drawing.Size(237, 18); + this.scriptontop.TabIndex = 40; + this.scriptontop.Text = "Script Editor always on top of main window"; + this.scriptontop.UseVisualStyleBackColor = true; + // + // zoomfactor + // + this.zoomfactor.LargeChange = 1; + this.zoomfactor.Location = new System.Drawing.Point(127, 187); + this.zoomfactor.Minimum = 1; + this.zoomfactor.Name = "zoomfactor"; + this.zoomfactor.Size = new System.Drawing.Size(116, 42); + this.zoomfactor.TabIndex = 37; + this.zoomfactor.TickStyle = System.Windows.Forms.TickStyle.Both; + this.zoomfactor.Value = 3; + this.zoomfactor.ValueChanged += new System.EventHandler(this.zoomfactor_ValueChanged); + // + // zoomfactorlabel + // + this.zoomfactorlabel.AutoSize = true; + this.zoomfactorlabel.Location = new System.Drawing.Point(249, 199); + this.zoomfactorlabel.Name = "zoomfactorlabel"; + this.zoomfactorlabel.Size = new System.Drawing.Size(29, 14); + this.zoomfactorlabel.TabIndex = 39; + this.zoomfactorlabel.Text = "30%"; + // + // label19 + // + this.label19.AutoSize = true; + this.label19.Location = new System.Drawing.Point(52, 199); + this.label19.Name = "label19"; + this.label19.Size = new System.Drawing.Size(69, 14); + this.label19.TabIndex = 38; + this.label19.Text = "Zoom factor:"; + // + // autoscrollspeed + // + this.autoscrollspeed.LargeChange = 1; + this.autoscrollspeed.Location = new System.Drawing.Point(127, 135); + this.autoscrollspeed.Maximum = 5; + this.autoscrollspeed.Name = "autoscrollspeed"; + this.autoscrollspeed.Size = new System.Drawing.Size(116, 42); + this.autoscrollspeed.TabIndex = 2; + this.autoscrollspeed.TickStyle = System.Windows.Forms.TickStyle.Both; + this.autoscrollspeed.ValueChanged += new System.EventHandler(this.autoscrollspeed_ValueChanged); + // + // autoscrollspeedlabel + // + this.autoscrollspeedlabel.AutoSize = true; + this.autoscrollspeedlabel.Location = new System.Drawing.Point(249, 147); + this.autoscrollspeedlabel.Name = "autoscrollspeedlabel"; + this.autoscrollspeedlabel.Size = new System.Drawing.Size(23, 14); + this.autoscrollspeedlabel.TabIndex = 36; + this.autoscrollspeedlabel.Text = "Off"; + // + // label15 + // + this.label15.AutoSize = true; + this.label15.Location = new System.Drawing.Point(29, 147); + this.label15.Name = "label15"; + this.label15.Size = new System.Drawing.Size(92, 14); + this.label15.TabIndex = 35; + this.label15.Text = "Autoscroll speed:"; + // + // previewsize + // + this.previewsize.LargeChange = 1; + this.previewsize.Location = new System.Drawing.Point(127, 81); + this.previewsize.Maximum = 5; + this.previewsize.Name = "previewsize"; + this.previewsize.Size = new System.Drawing.Size(116, 42); + this.previewsize.TabIndex = 1; + this.previewsize.TickStyle = System.Windows.Forms.TickStyle.Both; + this.previewsize.Value = 5; + this.previewsize.ValueChanged += new System.EventHandler(this.previewsize_ValueChanged); + // + // previewsizelabel + // + this.previewsizelabel.AutoSize = true; + this.previewsizelabel.Location = new System.Drawing.Point(249, 93); + this.previewsizelabel.Name = "previewsizelabel"; + this.previewsizelabel.Size = new System.Drawing.Size(55, 14); + this.previewsizelabel.TabIndex = 33; + this.previewsizelabel.Text = "128 x 128"; + // + // label12 + // + this.label12.AutoSize = true; + this.label12.Location = new System.Drawing.Point(17, 93); + this.label12.Name = "label12"; + this.label12.Size = new System.Drawing.Size(104, 14); + this.label12.TabIndex = 32; + this.label12.Text = "Preview image size:"; + // + // label14 + // + this.label14.AutoSize = true; + this.label14.Location = new System.Drawing.Point(50, 41); + this.label14.Name = "label14"; + this.label14.Size = new System.Drawing.Size(71, 14); + this.label14.TabIndex = 14; + this.label14.Text = "Default view:"; + // + // defaultviewmode + // + this.defaultviewmode.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.defaultviewmode.FormattingEnabled = true; + this.defaultviewmode.Items.AddRange(new object[] { + "Wireframe", + //"Brightness Levels", + "Floor Textures", + "Ceiling Textures"}); + this.defaultviewmode.Location = new System.Drawing.Point(135, 38); + this.defaultviewmode.Name = "defaultviewmode"; + this.defaultviewmode.Size = new System.Drawing.Size(145, 22); + this.defaultviewmode.TabIndex = 0; + // + // label1 + // + label1.AutoSize = true; + label1.Location = new System.Drawing.Point(22, 40); + label1.Name = "label1"; + label1.Size = new System.Drawing.Size(148, 14); + label1.TabIndex = 20; + label1.Text = "Texture and Flats brightness:"; + label1.TextAlign = System.Drawing.ContentAlignment.TopRight; + // + // keyusedlabel + // + this.keyusedlabel.AutoSize = true; + this.keyusedlabel.Location = new System.Drawing.Point(20, 287); + this.keyusedlabel.Name = "keyusedlabel"; + this.keyusedlabel.Size = new System.Drawing.Size(222, 14); + this.keyusedlabel.TabIndex = 10; + this.keyusedlabel.Text = "Key combination also used by these actions:"; + this.keyusedlabel.Visible = false; + // + // colorsgroup1 + // + this.colorsgroup1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left))); + this.colorsgroup1.Controls.Add(this.colorTagonly); + this.colorsgroup1.Controls.Add(this.colorSecret); + this.colorsgroup1.Controls.Add(this.colorMblock); + this.colorsgroup1.Controls.Add(this.coloinvisiblelinedef); + this.colorsgroup1.Controls.Add(this.colorgrid64); + this.colorsgroup1.Controls.Add(this.colorgrid); + this.colorsgroup1.Controls.Add(this.colorindication); + this.colorsgroup1.Controls.Add(this.colorsoundlinedefs); + this.colorsgroup1.Controls.Add(this.colorspeciallinedefs); + this.colorsgroup1.Controls.Add(this.colorbackcolor); + this.colorsgroup1.Controls.Add(this.colorselection); + this.colorsgroup1.Controls.Add(this.colorvertices); + this.colorsgroup1.Controls.Add(this.colorhighlight); + this.colorsgroup1.Controls.Add(this.colorlinedefs); + this.colorsgroup1.Location = new System.Drawing.Point(8, 8); + this.colorsgroup1.Name = "colorsgroup1"; + this.colorsgroup1.Size = new System.Drawing.Size(203, 472); + this.colorsgroup1.TabIndex = 0; + this.colorsgroup1.TabStop = false; + this.colorsgroup1.Text = " Display "; + this.colorsgroup1.Visible = false; + // + // colorTagonly + // + this.colorTagonly.BackColor = System.Drawing.Color.Transparent; + this.colorTagonly.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.colorTagonly.Label = "Tag only lines:"; + this.colorTagonly.Location = new System.Drawing.Point(15, 378); + this.colorTagonly.MaximumSize = new System.Drawing.Size(10000, 23); + this.colorTagonly.MinimumSize = new System.Drawing.Size(100, 23); + this.colorTagonly.Name = "colorTagonly"; + this.colorTagonly.Size = new System.Drawing.Size(168, 23); + this.colorTagonly.TabIndex = 17; + // + // colorSecret + // + this.colorSecret.BackColor = System.Drawing.Color.Transparent; + this.colorSecret.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.colorSecret.Label = "Secret lines:"; + this.colorSecret.Location = new System.Drawing.Point(15, 351); + this.colorSecret.MaximumSize = new System.Drawing.Size(10000, 23); + this.colorSecret.MinimumSize = new System.Drawing.Size(100, 23); + this.colorSecret.Name = "colorSecret"; + this.colorSecret.Size = new System.Drawing.Size(168, 23); + this.colorSecret.TabIndex = 16; + // + // colorMblock + // + this.colorMblock.BackColor = System.Drawing.Color.Transparent; + this.colorMblock.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.colorMblock.Label = "Monster block:"; + this.colorMblock.Location = new System.Drawing.Point(15, 324); + this.colorMblock.MaximumSize = new System.Drawing.Size(10000, 23); + this.colorMblock.MinimumSize = new System.Drawing.Size(100, 23); + this.colorMblock.Name = "colorMblock"; + this.colorMblock.Size = new System.Drawing.Size(168, 23); + this.colorMblock.TabIndex = 15; + // + // coloinvisiblelinedef + // + this.coloinvisiblelinedef.BackColor = System.Drawing.Color.Transparent; + this.coloinvisiblelinedef.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.coloinvisiblelinedef.Label = "Invisible lines:"; + this.coloinvisiblelinedef.Location = new System.Drawing.Point(15, 297); + this.coloinvisiblelinedef.MaximumSize = new System.Drawing.Size(10000, 23); + this.coloinvisiblelinedef.MinimumSize = new System.Drawing.Size(100, 23); + this.coloinvisiblelinedef.Name = "coloinvisiblelinedef"; + this.coloinvisiblelinedef.Size = new System.Drawing.Size(168, 23); + this.coloinvisiblelinedef.TabIndex = 14; + // + // colorgrid64 + // + this.colorgrid64.BackColor = System.Drawing.Color.Transparent; + this.colorgrid64.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.colorgrid64.Label = "64 Block grid:"; + this.colorgrid64.Location = new System.Drawing.Point(15, 270); + this.colorgrid64.MaximumSize = new System.Drawing.Size(10000, 23); + this.colorgrid64.MinimumSize = new System.Drawing.Size(100, 23); + this.colorgrid64.Name = "colorgrid64"; + this.colorgrid64.Size = new System.Drawing.Size(168, 23); + this.colorgrid64.TabIndex = 9; + // + // colorgrid + // + this.colorgrid.BackColor = System.Drawing.Color.Transparent; + this.colorgrid.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.colorgrid.Label = "Custom grid:"; + this.colorgrid.Location = new System.Drawing.Point(15, 243); + this.colorgrid.MaximumSize = new System.Drawing.Size(10000, 23); + this.colorgrid.MinimumSize = new System.Drawing.Size(100, 23); + this.colorgrid.Name = "colorgrid"; + this.colorgrid.Size = new System.Drawing.Size(168, 23); + this.colorgrid.TabIndex = 8; + // + // colorindication + // + this.colorindication.BackColor = System.Drawing.Color.Transparent; + this.colorindication.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.colorindication.Label = "Indications:"; + this.colorindication.Location = new System.Drawing.Point(15, 216); + this.colorindication.MaximumSize = new System.Drawing.Size(10000, 23); + this.colorindication.MinimumSize = new System.Drawing.Size(100, 23); + this.colorindication.Name = "colorindication"; + this.colorindication.Size = new System.Drawing.Size(168, 23); + this.colorindication.TabIndex = 7; + // + // colorsoundlinedefs + // + this.colorsoundlinedefs.BackColor = System.Drawing.Color.Transparent; + this.colorsoundlinedefs.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.colorsoundlinedefs.Label = "Sound lines:"; + this.colorsoundlinedefs.Location = new System.Drawing.Point(15, 135); + this.colorsoundlinedefs.MaximumSize = new System.Drawing.Size(10000, 23); + this.colorsoundlinedefs.MinimumSize = new System.Drawing.Size(100, 23); + this.colorsoundlinedefs.Name = "colorsoundlinedefs"; + this.colorsoundlinedefs.Size = new System.Drawing.Size(168, 23); + this.colorsoundlinedefs.TabIndex = 4; + // + // colorspeciallinedefs + // + this.colorspeciallinedefs.BackColor = System.Drawing.Color.Transparent; + this.colorspeciallinedefs.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.colorspeciallinedefs.Label = "Action lines:"; + this.colorspeciallinedefs.Location = new System.Drawing.Point(15, 108); + this.colorspeciallinedefs.MaximumSize = new System.Drawing.Size(10000, 23); + this.colorspeciallinedefs.MinimumSize = new System.Drawing.Size(100, 23); + this.colorspeciallinedefs.Name = "colorspeciallinedefs"; + this.colorspeciallinedefs.Size = new System.Drawing.Size(168, 23); + this.colorspeciallinedefs.TabIndex = 3; + // + // colorbackcolor + // + this.colorbackcolor.BackColor = System.Drawing.Color.Transparent; + this.colorbackcolor.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.colorbackcolor.Label = "Background:"; + this.colorbackcolor.Location = new System.Drawing.Point(15, 27); + this.colorbackcolor.MaximumSize = new System.Drawing.Size(10000, 23); + this.colorbackcolor.MinimumSize = new System.Drawing.Size(100, 23); + this.colorbackcolor.Name = "colorbackcolor"; + this.colorbackcolor.Size = new System.Drawing.Size(168, 23); + this.colorbackcolor.TabIndex = 0; + // + // colorselection + // + this.colorselection.BackColor = System.Drawing.Color.Transparent; + this.colorselection.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.colorselection.Label = "Selection:"; + this.colorselection.Location = new System.Drawing.Point(15, 189); + this.colorselection.MaximumSize = new System.Drawing.Size(10000, 23); + this.colorselection.MinimumSize = new System.Drawing.Size(100, 23); + this.colorselection.Name = "colorselection"; + this.colorselection.Size = new System.Drawing.Size(168, 23); + this.colorselection.TabIndex = 6; + // + // colorvertices + // + this.colorvertices.BackColor = System.Drawing.Color.Transparent; + this.colorvertices.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.colorvertices.Label = "Vertices:"; + this.colorvertices.Location = new System.Drawing.Point(15, 54); + this.colorvertices.MaximumSize = new System.Drawing.Size(10000, 23); + this.colorvertices.MinimumSize = new System.Drawing.Size(100, 23); + this.colorvertices.Name = "colorvertices"; + this.colorvertices.Size = new System.Drawing.Size(168, 23); + this.colorvertices.TabIndex = 1; + // + // colorhighlight + // + this.colorhighlight.BackColor = System.Drawing.Color.Transparent; + this.colorhighlight.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.colorhighlight.Label = "Highlight:"; + this.colorhighlight.Location = new System.Drawing.Point(15, 162); + this.colorhighlight.MaximumSize = new System.Drawing.Size(10000, 23); + this.colorhighlight.MinimumSize = new System.Drawing.Size(100, 23); + this.colorhighlight.Name = "colorhighlight"; + this.colorhighlight.Size = new System.Drawing.Size(168, 23); + this.colorhighlight.TabIndex = 5; + // + // colorlinedefs + // + this.colorlinedefs.BackColor = System.Drawing.Color.Transparent; + this.colorlinedefs.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.colorlinedefs.Label = "Common lines:"; + this.colorlinedefs.Location = new System.Drawing.Point(15, 81); + this.colorlinedefs.MaximumSize = new System.Drawing.Size(10000, 23); + this.colorlinedefs.MinimumSize = new System.Drawing.Size(100, 23); + this.colorlinedefs.Name = "colorlinedefs"; + this.colorlinedefs.Size = new System.Drawing.Size(168, 23); + this.colorlinedefs.TabIndex = 2; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(22, 85); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(147, 14); + this.label2.TabIndex = 14; + this.label2.Text = "Passable lines transparency:"; + this.label2.TextAlign = System.Drawing.ContentAlignment.TopRight; + // + // squarethings + // + this.squarethings.AutoSize = true; + this.squarethings.Location = new System.Drawing.Point(236, 120); + this.squarethings.Name = "squarethings"; + this.squarethings.Size = new System.Drawing.Size(93, 18); + this.squarethings.TabIndex = 8; + this.squarethings.Text = "Square things"; + this.squarethings.UseVisualStyleBackColor = true; + // + // doublesidedalphalabel + // + this.doublesidedalphalabel.AutoSize = true; + this.doublesidedalphalabel.Location = new System.Drawing.Point(337, 85); + this.doublesidedalphalabel.Name = "doublesidedalphalabel"; + this.doublesidedalphalabel.Size = new System.Drawing.Size(23, 14); + this.doublesidedalphalabel.TabIndex = 16; + this.doublesidedalphalabel.Text = "0%"; + // + // qualitydisplay + // + this.qualitydisplay.AutoSize = true; + this.qualitydisplay.Location = new System.Drawing.Point(25, 120); + this.qualitydisplay.Name = "qualitydisplay"; + this.qualitydisplay.Size = new System.Drawing.Size(130, 18); + this.qualitydisplay.TabIndex = 7; + this.qualitydisplay.Text = "High quality rendering"; + this.qualitydisplay.UseVisualStyleBackColor = true; + // + // doublesidedalpha + // + this.doublesidedalpha.LargeChange = 3; + this.doublesidedalpha.Location = new System.Drawing.Point(180, 72); + this.doublesidedalpha.Name = "doublesidedalpha"; + this.doublesidedalpha.Size = new System.Drawing.Size(150, 42); + this.doublesidedalpha.TabIndex = 2; + this.doublesidedalpha.TickStyle = System.Windows.Forms.TickStyle.Both; + this.doublesidedalpha.ValueChanged += new System.EventHandler(this.doublesidedalpha_ValueChanged); + // + // cancel + // + this.cancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.cancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.cancel.Location = new System.Drawing.Point(589, 537); + this.cancel.Name = "cancel"; + this.cancel.Size = new System.Drawing.Size(112, 25); + this.cancel.TabIndex = 2; + this.cancel.Text = "Cancel"; + this.cancel.UseVisualStyleBackColor = true; + this.cancel.Click += new System.EventHandler(this.cancel_Click); + // + // apply + // + this.apply.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.apply.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.apply.Location = new System.Drawing.Point(471, 537); + this.apply.Name = "apply"; + this.apply.Size = new System.Drawing.Size(112, 25); + this.apply.TabIndex = 1; + this.apply.Text = "OK"; + this.apply.UseVisualStyleBackColor = true; + this.apply.Click += new System.EventHandler(this.apply_Click); + // + // tabs + // + this.tabs.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.tabs.Controls.Add(this.tabinterface); + this.tabs.Controls.Add(this.tabkeys); + this.tabs.Controls.Add(this.tabcolors); + this.tabs.Controls.Add(this.tabpasting); + this.tabs.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tabs.ItemSize = new System.Drawing.Size(110, 19); + this.tabs.Location = new System.Drawing.Point(11, 13); + this.tabs.Name = "tabs"; + this.tabs.Padding = new System.Drawing.Point(20, 3); + this.tabs.SelectedIndex = 0; + this.tabs.Size = new System.Drawing.Size(690, 515); + this.tabs.TabIndex = 0; + this.tabs.SelectedIndexChanged += new System.EventHandler(this.tabs_SelectedIndexChanged); + // + // tabinterface + // + this.tabinterface.Controls.Add(this.groupBox5); + this.tabinterface.Controls.Add(this.groupBox4); + this.tabinterface.Controls.Add(this.groupBox2); + this.tabinterface.Controls.Add(groupBox1); + this.tabinterface.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tabinterface.Location = new System.Drawing.Point(4, 23); + this.tabinterface.Name = "tabinterface"; + this.tabinterface.Padding = new System.Windows.Forms.Padding(5); + this.tabinterface.Size = new System.Drawing.Size(682, 488); + this.tabinterface.TabIndex = 0; + this.tabinterface.Text = "Interface"; + this.tabinterface.UseVisualStyleBackColor = true; + // + // groupBox5 + // + this.groupBox5.Controls.Add(this.toolbar_file); + this.groupBox5.Controls.Add(this.toolbar_testing); + this.groupBox5.Controls.Add(this.toolbar_geometry); + this.groupBox5.Controls.Add(this.toolbar_viewmodes); + this.groupBox5.Controls.Add(this.toolbar_filter); + this.groupBox5.Controls.Add(this.toolbar_prefabs); + this.groupBox5.Controls.Add(this.toolbar_copy); + this.groupBox5.Controls.Add(this.toolbar_undo); + this.groupBox5.Controls.Add(this.toolbar_script); + this.groupBox5.Location = new System.Drawing.Point(8, 300); + this.groupBox5.Name = "groupBox5"; + this.groupBox5.Size = new System.Drawing.Size(331, 180); + this.groupBox5.TabIndex = 4; + this.groupBox5.TabStop = false; + this.groupBox5.Text = " Toolbar Buttons "; + // + // toolbar_file + // + this.toolbar_file.AutoSize = true; + this.toolbar_file.Location = new System.Drawing.Point(32, 36); + this.toolbar_file.Name = "toolbar_file"; + this.toolbar_file.Size = new System.Drawing.Size(118, 18); + this.toolbar_file.TabIndex = 49; + this.toolbar_file.Text = "New / Open / Save"; + this.toolbar_file.UseVisualStyleBackColor = true; + // + // toolbar_testing + // + this.toolbar_testing.AutoSize = true; + this.toolbar_testing.Location = new System.Drawing.Point(188, 114); + this.toolbar_testing.Name = "toolbar_testing"; + this.toolbar_testing.Size = new System.Drawing.Size(61, 18); + this.toolbar_testing.TabIndex = 48; + this.toolbar_testing.Text = "Testing"; + this.toolbar_testing.UseVisualStyleBackColor = true; + // + // toolbar_geometry + // + this.toolbar_geometry.AutoSize = true; + this.toolbar_geometry.Location = new System.Drawing.Point(188, 88); + this.toolbar_geometry.Name = "toolbar_geometry"; + this.toolbar_geometry.Size = new System.Drawing.Size(90, 18); + this.toolbar_geometry.TabIndex = 47; + this.toolbar_geometry.Text = "Snap / Merge"; + this.toolbar_geometry.UseVisualStyleBackColor = true; + // + // toolbar_viewmodes + // + this.toolbar_viewmodes.AutoSize = true; + this.toolbar_viewmodes.Location = new System.Drawing.Point(188, 62); + this.toolbar_viewmodes.Name = "toolbar_viewmodes"; + this.toolbar_viewmodes.Size = new System.Drawing.Size(87, 18); + this.toolbar_viewmodes.TabIndex = 46; + this.toolbar_viewmodes.Text = "View Modes"; + this.toolbar_viewmodes.UseVisualStyleBackColor = true; + // + // toolbar_filter + // + this.toolbar_filter.AutoSize = true; + this.toolbar_filter.Location = new System.Drawing.Point(188, 36); + this.toolbar_filter.Name = "toolbar_filter"; + this.toolbar_filter.Size = new System.Drawing.Size(84, 18); + this.toolbar_filter.TabIndex = 45; + this.toolbar_filter.Text = "Things Filter"; + this.toolbar_filter.UseVisualStyleBackColor = true; + // + // toolbar_prefabs + // + this.toolbar_prefabs.AutoSize = true; + this.toolbar_prefabs.Location = new System.Drawing.Point(32, 140); + this.toolbar_prefabs.Name = "toolbar_prefabs"; + this.toolbar_prefabs.Size = new System.Drawing.Size(64, 18); + this.toolbar_prefabs.TabIndex = 44; + this.toolbar_prefabs.Text = "Prefabs"; + this.toolbar_prefabs.UseVisualStyleBackColor = true; + // + // toolbar_copy + // + this.toolbar_copy.AutoSize = true; + this.toolbar_copy.Location = new System.Drawing.Point(32, 114); + this.toolbar_copy.Name = "toolbar_copy"; + this.toolbar_copy.Size = new System.Drawing.Size(112, 18); + this.toolbar_copy.TabIndex = 43; + this.toolbar_copy.Text = "Cut / Copy / Paste"; + this.toolbar_copy.UseVisualStyleBackColor = true; + // + // toolbar_undo + // + this.toolbar_undo.AutoSize = true; + this.toolbar_undo.Location = new System.Drawing.Point(32, 88); + this.toolbar_undo.Name = "toolbar_undo"; + this.toolbar_undo.Size = new System.Drawing.Size(85, 18); + this.toolbar_undo.TabIndex = 42; + this.toolbar_undo.Text = "Undo / Redo"; + this.toolbar_undo.UseVisualStyleBackColor = true; + // + // toolbar_script + // + this.toolbar_script.AutoSize = true; + this.toolbar_script.Location = new System.Drawing.Point(32, 62); + this.toolbar_script.Name = "toolbar_script"; + this.toolbar_script.Size = new System.Drawing.Size(84, 18); + this.toolbar_script.TabIndex = 41; + this.toolbar_script.Text = "Script Editor"; + this.toolbar_script.UseVisualStyleBackColor = true; + // + // groupBox4 + // + this.groupBox4.Controls.Add(this.collapsedockers); + this.groupBox4.Controls.Add(this.dockersposition); + this.groupBox4.Controls.Add(this.label17); + this.groupBox4.Location = new System.Drawing.Point(345, 300); + this.groupBox4.Name = "groupBox4"; + this.groupBox4.Size = new System.Drawing.Size(329, 180); + this.groupBox4.TabIndex = 3; + this.groupBox4.TabStop = false; + this.groupBox4.Text = " Side Panels "; + // + // collapsedockers + // + this.collapsedockers.AutoSize = true; + this.collapsedockers.Location = new System.Drawing.Point(204, 39); + this.collapsedockers.Name = "collapsedockers"; + this.collapsedockers.Size = new System.Drawing.Size(72, 18); + this.collapsedockers.TabIndex = 2; + this.collapsedockers.Text = "Auto hide"; + this.collapsedockers.UseVisualStyleBackColor = true; + // + // dockersposition + // + this.dockersposition.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.dockersposition.FormattingEnabled = true; + this.dockersposition.Items.AddRange(new object[] { + "Left", + "Right", + "None"}); + this.dockersposition.Location = new System.Drawing.Point(95, 37); + this.dockersposition.Name = "dockersposition"; + this.dockersposition.Size = new System.Drawing.Size(85, 22); + this.dockersposition.TabIndex = 1; + // + // label17 + // + this.label17.AutoSize = true; + this.label17.Location = new System.Drawing.Point(33, 40); + this.label17.Name = "label17"; + this.label17.Size = new System.Drawing.Size(47, 14); + this.label17.TabIndex = 0; + this.label17.Text = "Position:"; + // + // groupBox2 + // + this.groupBox2.Controls.Add(this.lightintensitylabel); + this.groupBox2.Controls.Add(this.lightintensity); + this.groupBox2.Controls.Add(this.label18); + this.groupBox2.Controls.Add(this.viewdistance); + this.groupBox2.Controls.Add(this.movespeed); + this.groupBox2.Controls.Add(this.mousespeed); + this.groupBox2.Controls.Add(this.fieldofview); + this.groupBox2.Controls.Add(this.viewdistancelabel); + this.groupBox2.Controls.Add(this.label13); + this.groupBox2.Controls.Add(this.invertyaxis); + this.groupBox2.Controls.Add(this.movespeedlabel); + this.groupBox2.Controls.Add(this.label11); + this.groupBox2.Controls.Add(this.mousespeedlabel); + this.groupBox2.Controls.Add(this.label9); + this.groupBox2.Controls.Add(this.fieldofviewlabel); + this.groupBox2.Controls.Add(this.label4); + this.groupBox2.Location = new System.Drawing.Point(345, 8); + this.groupBox2.Name = "groupBox2"; + this.groupBox2.Size = new System.Drawing.Size(329, 286); + this.groupBox2.TabIndex = 2; + this.groupBox2.TabStop = false; + this.groupBox2.Text = " Visual Modes "; + // + // lightintensitylabel + // + this.lightintensitylabel.AutoSize = true; + this.lightintensitylabel.Location = new System.Drawing.Point(264, 221); + this.lightintensitylabel.Name = "lightintensitylabel"; + this.lightintensitylabel.Size = new System.Drawing.Size(13, 14); + this.lightintensitylabel.TabIndex = 35; + this.lightintensitylabel.Text = "0"; + // + // lightintensity + // + this.lightintensity.Location = new System.Drawing.Point(108, 209); + this.lightintensity.Name = "lightintensity"; + this.lightintensity.Size = new System.Drawing.Size(150, 42); + this.lightintensity.TabIndex = 33; + this.lightintensity.TickStyle = System.Windows.Forms.TickStyle.Both; + this.lightintensity.ValueChanged += new System.EventHandler(this.lightintensity_ValueChanged); + // + // label18 + // + this.label18.AutoSize = true; + this.label18.Location = new System.Drawing.Point(27, 221); + this.label18.Name = "label18"; + this.label18.Size = new System.Drawing.Size(76, 14); + this.label18.TabIndex = 34; + this.label18.Text = "Light Intensity:"; + // + // viewdistance + // + this.viewdistance.LargeChange = 2; + this.viewdistance.Location = new System.Drawing.Point(108, 161); + this.viewdistance.Maximum = 15; + this.viewdistance.Minimum = 1; + this.viewdistance.Name = "viewdistance"; + this.viewdistance.Size = new System.Drawing.Size(150, 42); + this.viewdistance.TabIndex = 3; + this.viewdistance.TickStyle = System.Windows.Forms.TickStyle.Both; + this.viewdistance.Value = 1; + this.viewdistance.ValueChanged += new System.EventHandler(this.viewdistance_ValueChanged); + // + // movespeed + // + this.movespeed.Location = new System.Drawing.Point(108, 113); + this.movespeed.Maximum = 20; + this.movespeed.Minimum = 1; + this.movespeed.Name = "movespeed"; + this.movespeed.Size = new System.Drawing.Size(150, 42); + this.movespeed.TabIndex = 2; + this.movespeed.TickStyle = System.Windows.Forms.TickStyle.Both; + this.movespeed.Value = 1; + this.movespeed.ValueChanged += new System.EventHandler(this.movespeed_ValueChanged); + // + // mousespeed + // + this.mousespeed.Location = new System.Drawing.Point(108, 65); + this.mousespeed.Maximum = 20; + this.mousespeed.Minimum = 1; + this.mousespeed.Name = "mousespeed"; + this.mousespeed.Size = new System.Drawing.Size(150, 42); + this.mousespeed.TabIndex = 1; + this.mousespeed.TickStyle = System.Windows.Forms.TickStyle.Both; + this.mousespeed.Value = 1; + this.mousespeed.ValueChanged += new System.EventHandler(this.mousespeed_ValueChanged); + // + // fieldofview + // + this.fieldofview.LargeChange = 1; + this.fieldofview.Location = new System.Drawing.Point(108, 18); + this.fieldofview.Maximum = 17; + this.fieldofview.Minimum = 5; + this.fieldofview.Name = "fieldofview"; + this.fieldofview.Size = new System.Drawing.Size(150, 42); + this.fieldofview.TabIndex = 0; + this.fieldofview.TickStyle = System.Windows.Forms.TickStyle.Both; + this.fieldofview.Value = 5; + this.fieldofview.ValueChanged += new System.EventHandler(this.fieldofview_ValueChanged); + // + // viewdistancelabel + // + this.viewdistancelabel.AutoSize = true; + this.viewdistancelabel.Location = new System.Drawing.Point(264, 173); + this.viewdistancelabel.Name = "viewdistancelabel"; + this.viewdistancelabel.Size = new System.Drawing.Size(42, 14); + this.viewdistancelabel.TabIndex = 30; + this.viewdistancelabel.Text = "200 mp"; + // + // label13 + // + this.label13.AutoSize = true; + this.label13.Location = new System.Drawing.Point(22, 173); + this.label13.Name = "label13"; + this.label13.Size = new System.Drawing.Size(80, 14); + this.label13.TabIndex = 28; + this.label13.Text = "View distance:"; + // + // invertyaxis + // + this.invertyaxis.AutoSize = true; + this.invertyaxis.Location = new System.Drawing.Point(25, 257); + this.invertyaxis.Name = "invertyaxis"; + this.invertyaxis.Size = new System.Drawing.Size(122, 18); + this.invertyaxis.TabIndex = 4; + this.invertyaxis.Text = "Invert mouse Y axis"; + this.invertyaxis.UseVisualStyleBackColor = true; + // + // movespeedlabel + // + this.movespeedlabel.AutoSize = true; + this.movespeedlabel.Location = new System.Drawing.Point(264, 125); + this.movespeedlabel.Name = "movespeedlabel"; + this.movespeedlabel.Size = new System.Drawing.Size(25, 14); + this.movespeedlabel.TabIndex = 25; + this.movespeedlabel.Text = "100"; + // + // label11 + // + this.label11.AutoSize = true; + this.label11.Location = new System.Drawing.Point(33, 125); + this.label11.Name = "label11"; + this.label11.Size = new System.Drawing.Size(69, 14); + this.label11.TabIndex = 23; + this.label11.Text = "Move speed:"; + // + // mousespeedlabel + // + this.mousespeedlabel.AutoSize = true; + this.mousespeedlabel.Location = new System.Drawing.Point(264, 77); + this.mousespeedlabel.Name = "mousespeedlabel"; + this.mousespeedlabel.Size = new System.Drawing.Size(25, 14); + this.mousespeedlabel.TabIndex = 22; + this.mousespeedlabel.Text = "100"; + // + // label9 + // + this.label9.AutoSize = true; + this.label9.Location = new System.Drawing.Point(27, 77); + this.label9.Name = "label9"; + this.label9.Size = new System.Drawing.Size(75, 14); + this.label9.TabIndex = 20; + this.label9.Text = "Mouse speed:"; + // + // fieldofviewlabel + // + this.fieldofviewlabel.AutoSize = true; + this.fieldofviewlabel.Location = new System.Drawing.Point(264, 30); + this.fieldofviewlabel.Name = "fieldofviewlabel"; + this.fieldofviewlabel.Size = new System.Drawing.Size(23, 14); + this.fieldofviewlabel.TabIndex = 19; + this.fieldofviewlabel.Text = "50�"; + // + // label4 + // + this.label4.AutoSize = true; + this.label4.Location = new System.Drawing.Point(30, 30); + this.label4.Name = "label4"; + this.label4.Size = new System.Drawing.Size(72, 14); + this.label4.TabIndex = 17; + this.label4.Text = "Field of view:"; + // + // tabkeys + // + this.tabkeys.Controls.Add(this.listactions); + this.tabkeys.Controls.Add(this.actioncontrolpanel); + this.tabkeys.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tabkeys.Location = new System.Drawing.Point(4, 23); + this.tabkeys.Name = "tabkeys"; + this.tabkeys.Padding = new System.Windows.Forms.Padding(3); + this.tabkeys.Size = new System.Drawing.Size(682, 488); + this.tabkeys.TabIndex = 1; + this.tabkeys.Text = "Controls"; + this.tabkeys.UseVisualStyleBackColor = true; + // + // listactions + // + this.listactions.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.listactions.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.columncontrolaction, + this.columncontrolkey}); + this.listactions.FullRowSelect = true; + this.listactions.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.Nonclickable; + this.listactions.HideSelection = false; + this.listactions.Location = new System.Drawing.Point(11, 12); + this.listactions.Margin = new System.Windows.Forms.Padding(8, 9, 8, 9); + this.listactions.MultiSelect = false; + this.listactions.Name = "listactions"; + this.listactions.Size = new System.Drawing.Size(352, 462); + this.listactions.Sorting = System.Windows.Forms.SortOrder.Ascending; + this.listactions.TabIndex = 0; + this.listactions.TabStop = false; + this.listactions.UseCompatibleStateImageBehavior = false; + this.listactions.View = System.Windows.Forms.View.Details; + this.listactions.MouseUp += new System.Windows.Forms.MouseEventHandler(this.listactions_MouseUp); + this.listactions.ItemSelectionChanged += new System.Windows.Forms.ListViewItemSelectionChangedEventHandler(this.listactions_ItemSelectionChanged); + this.listactions.KeyUp += new System.Windows.Forms.KeyEventHandler(this.listactions_KeyUp); + // + // columncontrolaction + // + this.columncontrolaction.Text = "Action"; + this.columncontrolaction.Width = 179; + // + // columncontrolkey + // + this.columncontrolkey.Text = "Key"; + this.columncontrolkey.Width = 130; + // + // actioncontrolpanel + // + this.actioncontrolpanel.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Right))); + this.actioncontrolpanel.Controls.Add(this.keyusedlist); + this.actioncontrolpanel.Controls.Add(this.keyusedlabel); + this.actioncontrolpanel.Controls.Add(this.disregardshiftlabel); + this.actioncontrolpanel.Controls.Add(this.actioncontrol); + this.actioncontrolpanel.Controls.Add(label7); + this.actioncontrolpanel.Controls.Add(this.actiontitle); + this.actioncontrolpanel.Controls.Add(this.actioncontrolclear); + this.actioncontrolpanel.Controls.Add(label6); + this.actioncontrolpanel.Controls.Add(this.actionkey); + this.actioncontrolpanel.Controls.Add(this.actiondescription); + this.actioncontrolpanel.Controls.Add(label5); + this.actioncontrolpanel.Enabled = false; + this.actioncontrolpanel.Location = new System.Drawing.Point(377, 12); + this.actioncontrolpanel.Margin = new System.Windows.Forms.Padding(6); + this.actioncontrolpanel.Name = "actioncontrolpanel"; + this.actioncontrolpanel.Size = new System.Drawing.Size(282, 462); + this.actioncontrolpanel.TabIndex = 9; + this.actioncontrolpanel.TabStop = false; + this.actioncontrolpanel.Text = " Action control "; + // + // keyusedlist + // + this.keyusedlist.BackColor = System.Drawing.SystemColors.Control; + this.keyusedlist.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.keyusedlist.FormattingEnabled = true; + this.keyusedlist.IntegralHeight = false; + this.keyusedlist.ItemHeight = 14; + this.keyusedlist.Location = new System.Drawing.Point(33, 307); + this.keyusedlist.Name = "keyusedlist"; + this.keyusedlist.SelectionMode = System.Windows.Forms.SelectionMode.None; + this.keyusedlist.Size = new System.Drawing.Size(232, 115); + this.keyusedlist.Sorted = true; + this.keyusedlist.TabIndex = 11; + this.keyusedlist.Visible = false; + // + // disregardshiftlabel + // + this.disregardshiftlabel.Location = new System.Drawing.Point(20, 224); + this.disregardshiftlabel.Name = "disregardshiftlabel"; + this.disregardshiftlabel.Size = new System.Drawing.Size(245, 47); + this.disregardshiftlabel.TabIndex = 9; + this.disregardshiftlabel.Tag = "The selected actions uses %s to modify its behavior. These modifiers can not be u" + + "sed in a key combination for this action."; + this.disregardshiftlabel.Text = "The selected actions uses Shift, Alt and Control to modify its behavior. These mo" + + "difiers can not be used in a key combination for this action."; + this.disregardshiftlabel.Visible = false; + // + // actioncontrol + // + this.actioncontrol.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.actioncontrol.FormattingEnabled = true; + this.actioncontrol.ImeMode = System.Windows.Forms.ImeMode.Off; + this.actioncontrol.Location = new System.Drawing.Point(23, 190); + this.actioncontrol.Name = "actioncontrol"; + this.actioncontrol.Size = new System.Drawing.Size(197, 22); + this.actioncontrol.TabIndex = 8; + this.actioncontrol.TabStop = false; + this.actioncontrol.SelectedIndexChanged += new System.EventHandler(this.actioncontrol_SelectedIndexChanged); + // + // actiontitle + // + this.actiontitle.AutoSize = true; + this.actiontitle.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.actiontitle.Location = new System.Drawing.Point(67, 30); + this.actiontitle.Name = "actiontitle"; + this.actiontitle.Size = new System.Drawing.Size(172, 14); + this.actiontitle.TabIndex = 1; + this.actiontitle.Text = "(select an action from the list)"; + this.actiontitle.UseMnemonic = false; + // + // actioncontrolclear + // + this.actioncontrolclear.Location = new System.Drawing.Point(193, 138); + this.actioncontrolclear.Name = "actioncontrolclear"; + this.actioncontrolclear.Size = new System.Drawing.Size(63, 25); + this.actioncontrolclear.TabIndex = 6; + this.actioncontrolclear.TabStop = false; + this.actioncontrolclear.Text = "Clear"; + this.actioncontrolclear.UseVisualStyleBackColor = true; + this.actioncontrolclear.Click += new System.EventHandler(this.actioncontrolclear_Click); + // + // actionkey + // + this.actionkey.ImeMode = System.Windows.Forms.ImeMode.Off; + this.actionkey.Location = new System.Drawing.Point(23, 140); + this.actionkey.Name = "actionkey"; + this.actionkey.Size = new System.Drawing.Size(163, 20); + this.actionkey.TabIndex = 5; + this.actionkey.TabStop = false; + this.actionkey.KeyDown += new System.Windows.Forms.KeyEventHandler(this.actionkey_KeyDown); + // + // actiondescription + // + this.actiondescription.AutoEllipsis = true; + this.actiondescription.Location = new System.Drawing.Point(20, 50); + this.actiondescription.Name = "actiondescription"; + this.actiondescription.Size = new System.Drawing.Size(245, 71); + this.actiondescription.TabIndex = 3; + this.actiondescription.UseMnemonic = false; + // + // tabcolors + // + this.tabcolors.Controls.Add(this.appearancegroup1); + this.tabcolors.Controls.Add(this.colorsgroup3); + this.tabcolors.Controls.Add(this.colorsgroup1); + this.tabcolors.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tabcolors.Location = new System.Drawing.Point(4, 23); + this.tabcolors.Name = "tabcolors"; + this.tabcolors.Padding = new System.Windows.Forms.Padding(5); + this.tabcolors.Size = new System.Drawing.Size(682, 488); + this.tabcolors.TabIndex = 2; + this.tabcolors.Text = "Appearance"; + this.tabcolors.UseVisualStyleBackColor = true; + // + // appearancegroup1 + // + this.appearancegroup1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.appearancegroup1.Controls.Add(this.label2); + this.appearancegroup1.Controls.Add(this.squarethings); + this.appearancegroup1.Controls.Add(this.animatevisualselection); + this.appearancegroup1.Controls.Add(this.blackbrowsers); + this.appearancegroup1.Controls.Add(this.visualbilinear); + this.appearancegroup1.Controls.Add(label1); + this.appearancegroup1.Controls.Add(this.doublesidedalphalabel); + this.appearancegroup1.Controls.Add(this.qualitydisplay); + this.appearancegroup1.Controls.Add(this.classicbilinear); + this.appearancegroup1.Controls.Add(this.imagebrightnesslabel); + this.appearancegroup1.Controls.Add(this.imagebrightness); + this.appearancegroup1.Controls.Add(this.doublesidedalpha); + this.appearancegroup1.Location = new System.Drawing.Point(217, 251); + this.appearancegroup1.Name = "appearancegroup1"; + this.appearancegroup1.Size = new System.Drawing.Size(457, 229); + this.appearancegroup1.TabIndex = 24; + this.appearancegroup1.TabStop = false; + this.appearancegroup1.Text = " Additional Options "; + // + // animatevisualselection + // + this.animatevisualselection.AutoSize = true; + this.animatevisualselection.Location = new System.Drawing.Point(236, 152); + this.animatevisualselection.Name = "animatevisualselection"; + this.animatevisualselection.Size = new System.Drawing.Size(188, 18); + this.animatevisualselection.TabIndex = 23; + this.animatevisualselection.Text = "Animate selection in visual modes"; + this.animatevisualselection.UseVisualStyleBackColor = true; + // + // blackbrowsers + // + this.blackbrowsers.AutoSize = true; + this.blackbrowsers.Location = new System.Drawing.Point(236, 184); + this.blackbrowsers.Name = "blackbrowsers"; + this.blackbrowsers.Size = new System.Drawing.Size(199, 18); + this.blackbrowsers.TabIndex = 4; + this.blackbrowsers.Text = "Black background in image browser"; + this.blackbrowsers.UseVisualStyleBackColor = true; + // + // visualbilinear + // + this.visualbilinear.AutoSize = true; + this.visualbilinear.Location = new System.Drawing.Point(25, 184); + this.visualbilinear.Name = "visualbilinear"; + this.visualbilinear.Size = new System.Drawing.Size(176, 18); + this.visualbilinear.TabIndex = 6; + this.visualbilinear.Text = "Bilinear filtering in visual modes"; + this.visualbilinear.UseVisualStyleBackColor = true; + // + // classicbilinear + // + this.classicbilinear.AutoSize = true; + this.classicbilinear.Location = new System.Drawing.Point(25, 152); + this.classicbilinear.Name = "classicbilinear"; + this.classicbilinear.Size = new System.Drawing.Size(182, 18); + this.classicbilinear.TabIndex = 5; + this.classicbilinear.Text = "Bilinear filtering in classic modes"; + this.classicbilinear.UseVisualStyleBackColor = true; + // + // imagebrightnesslabel + // + this.imagebrightnesslabel.AutoSize = true; + this.imagebrightnesslabel.Location = new System.Drawing.Point(337, 40); + this.imagebrightnesslabel.Name = "imagebrightnesslabel"; + this.imagebrightnesslabel.Size = new System.Drawing.Size(31, 14); + this.imagebrightnesslabel.TabIndex = 22; + this.imagebrightnesslabel.Text = "+ 0 y"; + // + // imagebrightness + // + this.imagebrightness.LargeChange = 3; + this.imagebrightness.Location = new System.Drawing.Point(176, 27); + this.imagebrightness.Name = "imagebrightness"; + this.imagebrightness.Size = new System.Drawing.Size(154, 42); + this.imagebrightness.TabIndex = 3; + this.imagebrightness.TickStyle = System.Windows.Forms.TickStyle.Both; + this.imagebrightness.ValueChanged += new System.EventHandler(this.imagebrightness_ValueChanged); + // + // colorsgroup3 + // + this.colorsgroup3.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.colorsgroup3.Controls.Add(this.scripttabwidth); + this.colorsgroup3.Controls.Add(this.scriptautoindent); + this.colorsgroup3.Controls.Add(this.label10); + this.colorsgroup3.Controls.Add(this.panel1); + this.colorsgroup3.Controls.Add(this.scriptfontsize); + this.colorsgroup3.Controls.Add(this.label8); + this.colorsgroup3.Controls.Add(this.scriptfontbold); + this.colorsgroup3.Controls.Add(this.scriptfontname); + this.colorsgroup3.Controls.Add(this.label3); + this.colorsgroup3.Controls.Add(this.colorconstants); + this.colorsgroup3.Controls.Add(this.colorliterals); + this.colorsgroup3.Controls.Add(this.colorscriptbackground); + this.colorsgroup3.Controls.Add(this.colorkeywords); + this.colorsgroup3.Controls.Add(this.colorlinenumbers); + this.colorsgroup3.Controls.Add(this.colorcomments); + this.colorsgroup3.Controls.Add(this.colorplaintext); + this.colorsgroup3.Location = new System.Drawing.Point(217, 8); + this.colorsgroup3.Name = "colorsgroup3"; + this.colorsgroup3.Size = new System.Drawing.Size(457, 237); + this.colorsgroup3.TabIndex = 1; + this.colorsgroup3.TabStop = false; + this.colorsgroup3.Text = " Script editor "; + this.colorsgroup3.Visible = false; + // + // scripttabwidth + // + this.scripttabwidth.AllowDecimal = false; + this.scripttabwidth.AllowNegative = false; + this.scripttabwidth.AllowRelative = false; + this.scripttabwidth.ButtonStep = 2; + this.scripttabwidth.Location = new System.Drawing.Point(259, 165); + this.scripttabwidth.Name = "scripttabwidth"; + this.scripttabwidth.Size = new System.Drawing.Size(71, 24); + this.scripttabwidth.StepValues = null; + this.scripttabwidth.TabIndex = 32; + // + // scriptautoindent + // + this.scriptautoindent.AutoSize = true; + this.scriptautoindent.Location = new System.Drawing.Point(354, 169); + this.scriptautoindent.Name = "scriptautoindent"; + this.scriptautoindent.Size = new System.Drawing.Size(81, 18); + this.scriptautoindent.TabIndex = 31; + this.scriptautoindent.Text = "Auto indent"; + this.scriptautoindent.UseVisualStyleBackColor = true; + // + // label10 + // + this.label10.AutoSize = true; + this.label10.Location = new System.Drawing.Point(199, 170); + this.label10.Name = "label10"; + this.label10.Size = new System.Drawing.Size(58, 14); + this.label10.TabIndex = 30; + this.label10.Text = "Tab width:"; + // + // panel1 + // + this.panel1.BackColor = System.Drawing.SystemColors.Window; + this.panel1.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D; + this.panel1.Controls.Add(this.scriptfontlabel); + this.panel1.Location = new System.Drawing.Point(236, 109); + this.panel1.Name = "panel1"; + this.panel1.Size = new System.Drawing.Size(199, 38); + this.panel1.TabIndex = 29; + // + // scriptfontlabel + // + this.scriptfontlabel.BackColor = System.Drawing.SystemColors.Window; + this.scriptfontlabel.Dock = System.Windows.Forms.DockStyle.Fill; + this.scriptfontlabel.Location = new System.Drawing.Point(0, 0); + this.scriptfontlabel.Name = "scriptfontlabel"; + this.scriptfontlabel.Size = new System.Drawing.Size(195, 34); + this.scriptfontlabel.TabIndex = 0; + this.scriptfontlabel.Text = "Font"; + this.scriptfontlabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; + // + // scriptfontsize + // + this.scriptfontsize.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.scriptfontsize.FormattingEnabled = true; + this.scriptfontsize.Items.AddRange(new object[] { + "7", + "8", + "9", + "10", + "11", + "12", + "14", + "16", + "18", + "20", + "22", + "24", + "26", + "28", + "36", + "48", + "72"}); + this.scriptfontsize.Location = new System.Drawing.Point(236, 67); + this.scriptfontsize.Name = "scriptfontsize"; + this.scriptfontsize.Size = new System.Drawing.Size(94, 22); + this.scriptfontsize.TabIndex = 25; + // + // label8 + // + this.label8.AutoSize = true; + this.label8.Location = new System.Drawing.Point(199, 70); + this.label8.Name = "label8"; + this.label8.Size = new System.Drawing.Size(31, 14); + this.label8.TabIndex = 28; + this.label8.Text = "Size:"; + // + // scriptfontbold + // + this.scriptfontbold.AutoSize = true; + this.scriptfontbold.Location = new System.Drawing.Point(354, 69); + this.scriptfontbold.Name = "scriptfontbold"; + this.scriptfontbold.Size = new System.Drawing.Size(47, 18); + this.scriptfontbold.TabIndex = 26; + this.scriptfontbold.Text = "Bold"; + this.scriptfontbold.UseVisualStyleBackColor = true; + // + // scriptfontname + // + this.scriptfontname.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.scriptfontname.FormattingEnabled = true; + this.scriptfontname.Location = new System.Drawing.Point(236, 27); + this.scriptfontname.Name = "scriptfontname"; + this.scriptfontname.Size = new System.Drawing.Size(199, 22); + this.scriptfontname.Sorted = true; + this.scriptfontname.TabIndex = 23; + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Location = new System.Drawing.Point(199, 30); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(31, 14); + this.label3.TabIndex = 24; + this.label3.Text = "Font:"; + // + // colorconstants + // + this.colorconstants.BackColor = System.Drawing.Color.Transparent; + this.colorconstants.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.colorconstants.Label = "Constants:"; + this.colorconstants.Location = new System.Drawing.Point(15, 189); + this.colorconstants.MaximumSize = new System.Drawing.Size(10000, 23); + this.colorconstants.MinimumSize = new System.Drawing.Size(100, 23); + this.colorconstants.Name = "colorconstants"; + this.colorconstants.Size = new System.Drawing.Size(150, 23); + this.colorconstants.TabIndex = 6; + // + // colorliterals + // + this.colorliterals.BackColor = System.Drawing.Color.Transparent; + this.colorliterals.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.colorliterals.Label = "Literals:"; + this.colorliterals.Location = new System.Drawing.Point(15, 162); + this.colorliterals.MaximumSize = new System.Drawing.Size(10000, 23); + this.colorliterals.MinimumSize = new System.Drawing.Size(100, 23); + this.colorliterals.Name = "colorliterals"; + this.colorliterals.Size = new System.Drawing.Size(150, 23); + this.colorliterals.TabIndex = 5; + // + // colorscriptbackground + // + this.colorscriptbackground.BackColor = System.Drawing.Color.Transparent; + this.colorscriptbackground.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.colorscriptbackground.Label = "Background:"; + this.colorscriptbackground.Location = new System.Drawing.Point(15, 27); + this.colorscriptbackground.MaximumSize = new System.Drawing.Size(10000, 23); + this.colorscriptbackground.MinimumSize = new System.Drawing.Size(100, 23); + this.colorscriptbackground.Name = "colorscriptbackground"; + this.colorscriptbackground.Size = new System.Drawing.Size(150, 23); + this.colorscriptbackground.TabIndex = 0; + // + // colorkeywords + // + this.colorkeywords.BackColor = System.Drawing.Color.Transparent; + this.colorkeywords.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.colorkeywords.Label = "Keywords:"; + this.colorkeywords.Location = new System.Drawing.Point(15, 135); + this.colorkeywords.MaximumSize = new System.Drawing.Size(10000, 23); + this.colorkeywords.MinimumSize = new System.Drawing.Size(100, 23); + this.colorkeywords.Name = "colorkeywords"; + this.colorkeywords.Size = new System.Drawing.Size(150, 23); + this.colorkeywords.TabIndex = 4; + // + // colorlinenumbers + // + this.colorlinenumbers.BackColor = System.Drawing.Color.Transparent; + this.colorlinenumbers.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.colorlinenumbers.Label = "Line numbers:"; + this.colorlinenumbers.Location = new System.Drawing.Point(15, 54); + this.colorlinenumbers.MaximumSize = new System.Drawing.Size(10000, 23); + this.colorlinenumbers.MinimumSize = new System.Drawing.Size(100, 23); + this.colorlinenumbers.Name = "colorlinenumbers"; + this.colorlinenumbers.Size = new System.Drawing.Size(150, 23); + this.colorlinenumbers.TabIndex = 1; + // + // colorcomments + // + this.colorcomments.BackColor = System.Drawing.Color.Transparent; + this.colorcomments.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.colorcomments.Label = "Comments:"; + this.colorcomments.Location = new System.Drawing.Point(15, 108); + this.colorcomments.MaximumSize = new System.Drawing.Size(10000, 23); + this.colorcomments.MinimumSize = new System.Drawing.Size(100, 23); + this.colorcomments.Name = "colorcomments"; + this.colorcomments.Size = new System.Drawing.Size(150, 23); + this.colorcomments.TabIndex = 3; + // + // colorplaintext + // + this.colorplaintext.BackColor = System.Drawing.Color.Transparent; + this.colorplaintext.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.colorplaintext.Label = "Plain text:"; + this.colorplaintext.Location = new System.Drawing.Point(15, 81); + this.colorplaintext.MaximumSize = new System.Drawing.Size(10000, 23); + this.colorplaintext.MinimumSize = new System.Drawing.Size(100, 23); + this.colorplaintext.Name = "colorplaintext"; + this.colorplaintext.Size = new System.Drawing.Size(150, 23); + this.colorplaintext.TabIndex = 2; + // + // tabpasting + // + this.tabpasting.Controls.Add(this.label16); + this.tabpasting.Controls.Add(this.pasteoptions); + this.tabpasting.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tabpasting.Location = new System.Drawing.Point(4, 23); + this.tabpasting.Name = "tabpasting"; + this.tabpasting.Padding = new System.Windows.Forms.Padding(5); + this.tabpasting.Size = new System.Drawing.Size(682, 488); + this.tabpasting.TabIndex = 3; + this.tabpasting.Text = "Pasting "; + this.tabpasting.UseVisualStyleBackColor = true; + // + // label16 + // + this.label16.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.label16.Location = new System.Drawing.Point(11, 15); + this.label16.Name = "label16"; + this.label16.Size = new System.Drawing.Size(649, 35); + this.label16.TabIndex = 1; + this.label16.Text = "These are the default options for pasting geometry. You can also choose these opt" + + "ions when you use the Paste Special function. These options also apply when inse" + + "rting prefabs."; + // + // pasteoptions + // + this.pasteoptions.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.pasteoptions.Location = new System.Drawing.Point(8, 53); + this.pasteoptions.Name = "pasteoptions"; + this.pasteoptions.Size = new System.Drawing.Size(666, 427); + this.pasteoptions.TabIndex = 0; + // + // PreferencesForm + // + this.AcceptButton = this.apply; + this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; + this.CancelButton = this.apply; + this.ClientSize = new System.Drawing.Size(711, 573); + this.Controls.Add(this.cancel); + this.Controls.Add(this.apply); + this.Controls.Add(this.tabs); + this.DoubleBuffered = true; + this.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "PreferencesForm"; + this.Opacity = 0; + this.ShowIcon = false; + this.ShowInTaskbar = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Preferences"; + this.HelpRequested += new System.Windows.Forms.HelpEventHandler(this.PreferencesForm_HelpRequested); + groupBox1.ResumeLayout(false); + groupBox1.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.zoomfactor)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.autoscrollspeed)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.previewsize)).EndInit(); + this.colorsgroup1.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.doublesidedalpha)).EndInit(); + this.tabs.ResumeLayout(false); + this.tabinterface.ResumeLayout(false); + this.groupBox5.ResumeLayout(false); + this.groupBox5.PerformLayout(); + this.groupBox4.ResumeLayout(false); + this.groupBox4.PerformLayout(); + this.groupBox2.ResumeLayout(false); + this.groupBox2.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.lightintensity)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.viewdistance)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.movespeed)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.mousespeed)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.fieldofview)).EndInit(); + this.tabkeys.ResumeLayout(false); + this.actioncontrolpanel.ResumeLayout(false); + this.actioncontrolpanel.PerformLayout(); + this.tabcolors.ResumeLayout(false); + this.appearancegroup1.ResumeLayout(false); + this.appearancegroup1.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.imagebrightness)).EndInit(); + this.colorsgroup3.ResumeLayout(false); + this.colorsgroup3.PerformLayout(); + this.panel1.ResumeLayout(false); + this.tabpasting.ResumeLayout(false); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.Button cancel; + private System.Windows.Forms.Button apply; + private System.Windows.Forms.TabControl tabs; + private System.Windows.Forms.TabPage tabinterface; + private System.Windows.Forms.TabPage tabkeys; + private System.Windows.Forms.ListView listactions; + private System.Windows.Forms.ColumnHeader columncontrolaction; + private System.Windows.Forms.ColumnHeader columncontrolkey; + private System.Windows.Forms.GroupBox actioncontrolpanel; + private System.Windows.Forms.ComboBox actioncontrol; + private System.Windows.Forms.Label actiontitle; + private System.Windows.Forms.Button actioncontrolclear; + private System.Windows.Forms.TextBox actionkey; + private System.Windows.Forms.Label actiondescription; + private System.Windows.Forms.TabPage tabcolors; + private CodeImp.DoomBuilder.Controls.ColorControl colorselection; + private CodeImp.DoomBuilder.Controls.ColorControl colorhighlight; + private CodeImp.DoomBuilder.Controls.ColorControl colorlinedefs; + private CodeImp.DoomBuilder.Controls.ColorControl colorvertices; + private CodeImp.DoomBuilder.Controls.ColorControl colorbackcolor; + private System.Windows.Forms.GroupBox colorsgroup3; + private CodeImp.DoomBuilder.Controls.ColorControl colorscriptbackground; + private CodeImp.DoomBuilder.Controls.ColorControl colorkeywords; + private CodeImp.DoomBuilder.Controls.ColorControl colorlinenumbers; + private CodeImp.DoomBuilder.Controls.ColorControl colorcomments; + private CodeImp.DoomBuilder.Controls.ColorControl colorplaintext; + private CodeImp.DoomBuilder.Controls.ColorControl colorliterals; + private CodeImp.DoomBuilder.Controls.ColorControl colorconstants; + private CodeImp.DoomBuilder.Controls.ColorControl colorspeciallinedefs; + private CodeImp.DoomBuilder.Controls.ColorControl colorsoundlinedefs; + private CodeImp.DoomBuilder.Controls.ColorControl colorindication; + private CodeImp.DoomBuilder.Controls.ColorControl colorgrid64; + private CodeImp.DoomBuilder.Controls.ColorControl colorgrid; + private System.Windows.Forms.GroupBox colorsgroup1; + private System.Windows.Forms.CheckBox blackbrowsers; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.Label doublesidedalphalabel; + private System.Windows.Forms.Label imagebrightnesslabel; + private System.Windows.Forms.GroupBox groupBox2; + private System.Windows.Forms.Label fieldofviewlabel; + private System.Windows.Forms.Label label4; + private System.Windows.Forms.Label movespeedlabel; + private System.Windows.Forms.Label label11; + private System.Windows.Forms.Label mousespeedlabel; + private System.Windows.Forms.Label label9; + private System.Windows.Forms.Label viewdistancelabel; + private System.Windows.Forms.Label label13; + private System.Windows.Forms.CheckBox invertyaxis; + private System.Windows.Forms.ComboBox defaultviewmode; + private System.Windows.Forms.Label label14; + private CodeImp.DoomBuilder.Controls.TransparentTrackBar fieldofview; + private CodeImp.DoomBuilder.Controls.TransparentTrackBar movespeed; + private CodeImp.DoomBuilder.Controls.TransparentTrackBar mousespeed; + private CodeImp.DoomBuilder.Controls.TransparentTrackBar viewdistance; + private CodeImp.DoomBuilder.Controls.TransparentTrackBar doublesidedalpha; + private CodeImp.DoomBuilder.Controls.TransparentTrackBar imagebrightness; + private System.Windows.Forms.Label disregardshiftlabel; + private System.Windows.Forms.ListBox keyusedlist; + private System.Windows.Forms.Label keyusedlabel; + private System.Windows.Forms.CheckBox qualitydisplay; + private System.Windows.Forms.CheckBox visualbilinear; + private System.Windows.Forms.CheckBox classicbilinear; + private CodeImp.DoomBuilder.Controls.TransparentTrackBar previewsize; + private System.Windows.Forms.Label previewsizelabel; + private System.Windows.Forms.Label label12; + private System.Windows.Forms.CheckBox squarethings; + private CodeImp.DoomBuilder.Controls.TransparentTrackBar autoscrollspeed; + private System.Windows.Forms.Label autoscrollspeedlabel; + private System.Windows.Forms.Label label15; + private System.Windows.Forms.CheckBox animatevisualselection; + private System.Windows.Forms.TabPage tabpasting; + private CodeImp.DoomBuilder.Controls.PasteOptionsControl pasteoptions; + private System.Windows.Forms.Label label16; + private System.Windows.Forms.GroupBox appearancegroup1; + private System.Windows.Forms.GroupBox groupBox4; + private System.Windows.Forms.ComboBox dockersposition; + private System.Windows.Forms.Label label17; + private System.Windows.Forms.CheckBox collapsedockers; + private CodeImp.DoomBuilder.Controls.TransparentTrackBar zoomfactor; + private System.Windows.Forms.Label zoomfactorlabel; + private System.Windows.Forms.Label label19; + private System.Windows.Forms.GroupBox groupBox5; + private CodeImp.DoomBuilder.Controls.ButtonsNumericTextbox scripttabwidth; + private System.Windows.Forms.CheckBox scriptautoindent; + private System.Windows.Forms.Label label10; + private System.Windows.Forms.Panel panel1; + private System.Windows.Forms.Label scriptfontlabel; + private System.Windows.Forms.ComboBox scriptfontsize; + private System.Windows.Forms.Label label8; + private System.Windows.Forms.CheckBox scriptfontbold; + private System.Windows.Forms.ComboBox scriptfontname; + private System.Windows.Forms.Label label3; + private System.Windows.Forms.CheckBox scriptontop; + private System.Windows.Forms.CheckBox toolbar_script; + private System.Windows.Forms.CheckBox toolbar_copy; + private System.Windows.Forms.CheckBox toolbar_undo; + private System.Windows.Forms.CheckBox toolbar_viewmodes; + private System.Windows.Forms.CheckBox toolbar_filter; + private System.Windows.Forms.CheckBox toolbar_prefabs; + private System.Windows.Forms.CheckBox toolbar_geometry; + private System.Windows.Forms.CheckBox toolbar_testing; + private System.Windows.Forms.CheckBox toolbar_file; + private CodeImp.DoomBuilder.Controls.TransparentTrackBar lightintensity; + private System.Windows.Forms.Label label18; + private System.Windows.Forms.Label lightintensitylabel; + private CodeImp.DoomBuilder.Controls.ColorControl colorTagonly; + private CodeImp.DoomBuilder.Controls.ColorControl colorSecret; + private CodeImp.DoomBuilder.Controls.ColorControl colorMblock; + private CodeImp.DoomBuilder.Controls.ColorControl coloinvisiblelinedef; + } +} \ No newline at end of file diff --git a/Source/Core/Windows/PreferencesForm.cs b/Source/Core/Windows/PreferencesForm.cs new file mode 100644 index 0000000..b86749f --- /dev/null +++ b/Source/Core/Windows/PreferencesForm.cs @@ -0,0 +1,785 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Text; +using System.Windows.Forms; +using Microsoft.Win32; +using System.Diagnostics; +using CodeImp.DoomBuilder.Actions; +using CodeImp.DoomBuilder.Controls; +using System.Globalization; +using CodeImp.DoomBuilder.Data; +using System.IO; + +#endregion + +namespace CodeImp.DoomBuilder.Windows +{ + internal partial class PreferencesForm : DelayedForm + { + #region ================== Variables + + private PreferencesController controller; + private bool allowapplycontrol = false; + private bool disregardshift = false; + private bool disregardcontrol = false; + + private bool reloadresources = false; + + #endregion + + #region ================== Properties + + public bool ReloadResources { get { return reloadresources; } } + + #endregion + + #region ================== Constructor + + // Constructor + public PreferencesForm() + { + Actions.Action[] actions; + ListViewItem item; + + // Initialize + InitializeComponent(); + + // Interface + imagebrightness.Value = General.Settings.ImageBrightness; + squarethings.Checked = General.Settings.SquareThings; + doublesidedalpha.Value = (int)((1.0f - General.Settings.DoubleSidedAlpha) * 10.0f); + defaultviewmode.SelectedIndex = General.Settings.DefaultViewMode; + fieldofview.Value = General.Settings.VisualFOV / 10; + lightintensity.Value = General.Settings.LightIntensity; // villsa + mousespeed.Value = General.Settings.MouseSpeed / 100; + movespeed.Value = General.Settings.MoveSpeed / 100; + viewdistance.Value = General.Clamp((int)(General.Settings.ViewDistance / 200.0f), viewdistance.Minimum, viewdistance.Maximum); + invertyaxis.Checked = General.Settings.InvertYAxis; + scriptfontbold.Checked = General.Settings.ScriptFontBold; + scriptontop.Checked = General.Settings.ScriptOnTop; + scripttabwidth.Text = General.Settings.ScriptTabWidth.ToString(); + scriptautoindent.Checked = General.Settings.ScriptAutoIndent; + previewsize.Value = General.Settings.PreviewImageSize; + autoscrollspeed.Value = General.Settings.AutoScrollSpeed; + zoomfactor.Value = General.Settings.ZoomFactor; + animatevisualselection.Checked = General.Settings.AnimateVisualSelection; + dockersposition.SelectedIndex = General.Settings.DockersPosition; + collapsedockers.Checked = General.Settings.CollapseDockers; + toolbar_file.Checked = General.Settings.ToolbarFile; + toolbar_script.Checked = General.Settings.ToolbarScript; + toolbar_undo.Checked = General.Settings.ToolbarUndo; + toolbar_copy.Checked = General.Settings.ToolbarCopy; + toolbar_prefabs.Checked = General.Settings.ToolbarPrefabs; + toolbar_filter.Checked = General.Settings.ToolbarFilter; + toolbar_viewmodes.Checked = General.Settings.ToolbarViewModes; + toolbar_geometry.Checked = General.Settings.ToolbarGeometry; + toolbar_testing.Checked = General.Settings.ToolbarTesting; + + // Fill fonts list + scriptfontname.BeginUpdate(); + foreach (FontFamily ff in System.Drawing.FontFamily.Families) + scriptfontname.Items.Add(ff.Name); + scriptfontname.EndUpdate(); + + // Select script font name + for (int i = 0; i < scriptfontname.Items.Count; i++) + { + if (string.Compare(scriptfontname.Items[i].ToString(), General.Settings.ScriptFontName, true) == 0) + scriptfontname.SelectedIndex = i; + } + + // Select script font size + for (int i = 0; i < scriptfontsize.Items.Count; i++) + { + if (string.Compare(scriptfontsize.Items[i].ToString(), General.Settings.ScriptFontSize.ToString(CultureInfo.InvariantCulture), true) == 0) + scriptfontsize.SelectedIndex = i; + } + + // Fill actions list with categories + foreach (KeyValuePair c in General.Actions.Categories) + listactions.Groups.Add(c.Key, c.Value); + + // Fill list of actions + actions = General.Actions.GetAllActions(); + foreach (Actions.Action a in actions) + { + // Create item + item = listactions.Items.Add(a.Name, a.Title, 0); + item.SubItems.Add(Actions.Action.GetShortcutKeyDesc(a.ShortcutKey)); + item.SubItems[1].Tag = a.ShortcutKey; + + // Put in category, if the category exists + if (General.Actions.Categories.ContainsKey(a.Category)) + item.Group = listactions.Groups[a.Category]; + } + + // Set the colors + // TODO: Make this automated by using the collection + colorbackcolor.Color = General.Colors.Background; + colorvertices.Color = General.Colors.Vertices; + colorlinedefs.Color = General.Colors.Linedefs; + colorspeciallinedefs.Color = General.Colors.Actions; + coloinvisiblelinedef.Color = General.Colors.Invisible; // villsa + colorMblock.Color = General.Colors.MBlock; // villsa + colorSecret.Color = General.Colors.Secret; // villsa + colorTagonly.Color = General.Colors.Tagonly; // villsa + colorsoundlinedefs.Color = General.Colors.Sounds; + colorhighlight.Color = General.Colors.Highlight; + colorselection.Color = General.Colors.Selection; + colorindication.Color = General.Colors.Indication; + colorgrid.Color = General.Colors.Grid; + colorgrid64.Color = General.Colors.Grid64; + colorscriptbackground.Color = General.Colors.ScriptBackground; + colorlinenumbers.Color = General.Colors.LineNumbers; + colorplaintext.Color = General.Colors.PlainText; + colorcomments.Color = General.Colors.Comments; + colorkeywords.Color = General.Colors.Keywords; + colorliterals.Color = General.Colors.Literals; + colorconstants.Color = General.Colors.Constants; + blackbrowsers.Checked = General.Settings.BlackBrowsers; + classicbilinear.Checked = General.Settings.ClassicBilinear; + visualbilinear.Checked = General.Settings.VisualBilinear; + qualitydisplay.Checked = General.Settings.QualityDisplay; + + // Paste options + pasteoptions.Setup(General.Settings.PasteOptions.Copy()); + + // Allow plugins to add tabs + this.SuspendLayout(); + controller = new PreferencesController(this); + controller.AllowAddTab = true; + General.Plugins.OnShowPreferences(controller); + controller.AllowAddTab = false; + this.ResumeLayout(true); + + // Done + allowapplycontrol = true; + } + + #endregion + + #region ================== OK / Cancel + + // OK clicked + private void apply_Click(object sender, EventArgs e) + { + // Let the plugins know + controller.RaiseAccept(); + + // Check if we need to reload the resources + reloadresources |= (General.Settings.ImageBrightness != imagebrightness.Value); + reloadresources |= (General.Settings.PreviewImageSize != previewsize.Value); + + // Apply interface + General.Settings.ImageBrightness = imagebrightness.Value; + General.Settings.SquareThings = squarethings.Checked; + General.Settings.DoubleSidedAlpha = 1.0f - (float)(doublesidedalpha.Value * 0.1f); + General.Settings.DefaultViewMode = defaultviewmode.SelectedIndex; + General.Settings.VisualFOV = fieldofview.Value * 10; + General.Settings.LightIntensity = lightintensity.Value; // villsa + General.Settings.MouseSpeed = mousespeed.Value * 100; + General.Settings.MoveSpeed = movespeed.Value * 100; + General.Settings.ViewDistance = (float)viewdistance.Value * 200.0f; + General.Settings.InvertYAxis = invertyaxis.Checked; + General.Settings.ScriptFontBold = scriptfontbold.Checked; + General.Settings.ScriptFontName = scriptfontname.Text; + General.Settings.ScriptOnTop = scriptontop.Checked; + General.Settings.ScriptTabWidth = scripttabwidth.GetResult(General.Settings.ScriptTabWidth); + General.Settings.ScriptAutoIndent = scriptautoindent.Checked; + General.Settings.PreviewImageSize = previewsize.Value; + General.Settings.AutoScrollSpeed = autoscrollspeed.Value; + General.Settings.ZoomFactor = zoomfactor.Value; + General.Settings.AnimateVisualSelection = animatevisualselection.Checked; + General.Settings.DockersPosition = dockersposition.SelectedIndex; + General.Settings.CollapseDockers = collapsedockers.Checked; + General.Settings.ToolbarFile = toolbar_file.Checked; + General.Settings.ToolbarScript = toolbar_script.Checked; + General.Settings.ToolbarUndo = toolbar_undo.Checked; + General.Settings.ToolbarCopy = toolbar_copy.Checked; + General.Settings.ToolbarPrefabs = toolbar_prefabs.Checked; + General.Settings.ToolbarFilter = toolbar_filter.Checked; + General.Settings.ToolbarViewModes = toolbar_viewmodes.Checked; + General.Settings.ToolbarGeometry = toolbar_geometry.Checked; + General.Settings.ToolbarTesting = toolbar_testing.Checked; + + // Script font size + int fontsize = 8; + int.TryParse(scriptfontsize.Text, out fontsize); + General.Settings.ScriptFontSize = fontsize; + + // Apply control keys to actions + foreach (ListViewItem item in listactions.Items) + General.Actions[item.Name].SetShortcutKey((int)item.SubItems[1].Tag); + + // Apply the colors + // TODO: Make this automated by using the collection + General.Colors.Background = colorbackcolor.Color; + General.Colors.Vertices = colorvertices.Color; + General.Colors.Linedefs = colorlinedefs.Color; + General.Colors.Actions = colorspeciallinedefs.Color; + General.Colors.Sounds = colorsoundlinedefs.Color; + General.Colors.Invisible = coloinvisiblelinedef.Color; // villsa + General.Colors.MBlock = colorMblock.Color; // villsa + General.Colors.Secret = colorSecret.Color; // villsa + General.Colors.Tagonly = colorTagonly.Color; // villsa + General.Colors.Highlight = colorhighlight.Color; + General.Colors.Selection = colorselection.Color; + General.Colors.Indication = colorindication.Color; + General.Colors.Grid = colorgrid.Color; + General.Colors.Grid64 = colorgrid64.Color; + General.Colors.ScriptBackground = colorscriptbackground.Color; + General.Colors.LineNumbers = colorlinenumbers.Color; + General.Colors.PlainText = colorplaintext.Color; + General.Colors.Comments = colorcomments.Color; + General.Colors.Keywords = colorkeywords.Color; + General.Colors.Literals = colorliterals.Color; + General.Colors.Constants = colorconstants.Color; + General.Colors.CreateAssistColors(); + General.Settings.BlackBrowsers = blackbrowsers.Checked; + General.Settings.ClassicBilinear = classicbilinear.Checked; + General.Settings.VisualBilinear = visualbilinear.Checked; + General.Settings.QualityDisplay = qualitydisplay.Checked; + + // Paste options + General.Settings.PasteOptions = pasteoptions.GetOptions(); + + // Let the plugins know we're closing + General.Plugins.OnClosePreferences(controller); + + // Close + this.DialogResult = DialogResult.OK; + this.Close(); + } + + // Cancel clicked + private void cancel_Click(object sender, EventArgs e) + { + // Let the plugins know + controller.RaiseCancel(); + + // Let the plugins know we're closing + General.Plugins.OnClosePreferences(controller); + + // Close + this.DialogResult = DialogResult.Cancel; + this.Close(); + } + + #endregion + + #region ================== Tabs + + // This adds a tab page + public void AddTabPage(TabPage tab) + { + tabs.TabPages.Add(tab); + } + + // Tab changes + private void tabs_SelectedIndexChanged(object sender, EventArgs e) + { + // Enable/disable stuff with tabs + if (tabs.SelectedTab != tabkeys) this.AcceptButton = apply; else this.AcceptButton = null; + if (tabs.SelectedTab != tabkeys) this.CancelButton = cancel; else this.CancelButton = null; + colorsgroup1.Visible = (tabs.SelectedTab == tabcolors); + //colorsgroup2.Visible = (tabs.SelectedTab == tabcolors); + colorsgroup3.Visible = (tabs.SelectedTab == tabcolors); + } + + #endregion + + #region ================== Interface Panel + + private void previewsize_ValueChanged(object sender, EventArgs e) + { + int size = PreviewManager.PREVIEW_SIZES[previewsize.Value]; + previewsizelabel.Text = size.ToString() + " x " + size.ToString(); + } + + private void fieldofview_ValueChanged(object sender, EventArgs e) + { + int value = fieldofview.Value * 10; + fieldofviewlabel.Text = value.ToString() + (char)176; + } + + private void lightintensity_ValueChanged(object sender, EventArgs e) + { + float value = (float)lightintensity.Value / 10.0f; + lightintensitylabel.Text = value.ToString(); + } + + private void mousespeed_ValueChanged(object sender, EventArgs e) + { + int value = mousespeed.Value * 100; + mousespeedlabel.Text = value.ToString(); + } + + private void movespeed_ValueChanged(object sender, EventArgs e) + { + int value = movespeed.Value * 100; + movespeedlabel.Text = value.ToString(); + } + + private void viewdistance_ValueChanged(object sender, EventArgs e) + { + int value = viewdistance.Value * 200; + viewdistancelabel.Text = value.ToString() + " mp"; + } + + private void scriptfontname_SelectedIndexChanged(object sender, EventArgs e) + { + UpdateScriptFontPreview(); + } + + private void scriptfontsize_SelectedIndexChanged(object sender, EventArgs e) + { + UpdateScriptFontPreview(); + } + + private void scriptfontbold_CheckedChanged(object sender, EventArgs e) + { + UpdateScriptFontPreview(); + } + + private void autoscrollspeed_ValueChanged(object sender, EventArgs e) + { + if (autoscrollspeed.Value == 0) + autoscrollspeedlabel.Text = "Off"; + else + autoscrollspeedlabel.Text = autoscrollspeed.Value.ToString() + "x"; + } + + private void zoomfactor_ValueChanged(object sender, EventArgs e) + { + zoomfactorlabel.Text = (zoomfactor.Value * 10).ToString() + "%"; + } + + // This updates the script font preview label + private void UpdateScriptFontPreview() + { + if ((scriptfontname.SelectedIndex > -1) && + (scriptfontsize.SelectedIndex > -1)) + { + scriptfontlabel.Text = scriptfontname.Text; + scriptfontlabel.BackColor = General.Colors.ScriptBackground.ToColor(); + scriptfontlabel.ForeColor = General.Colors.PlainText.ToColor(); + FontFamily ff = new FontFamily(scriptfontname.Text); + FontStyle style = FontStyle.Regular; + if (scriptfontbold.Checked) + { + // Prefer bold over regular + if (ff.IsStyleAvailable(FontStyle.Bold)) + style = FontStyle.Bold; + else if (ff.IsStyleAvailable(FontStyle.Regular)) + style = FontStyle.Regular; + else if (ff.IsStyleAvailable(FontStyle.Italic)) + style = FontStyle.Italic; + else if (ff.IsStyleAvailable(FontStyle.Underline)) + style = FontStyle.Underline; + else if (ff.IsStyleAvailable(FontStyle.Strikeout)) + style = FontStyle.Strikeout; + } + else + { + // Prefer regular over bold + if (ff.IsStyleAvailable(FontStyle.Regular)) + style = FontStyle.Regular; + else if (ff.IsStyleAvailable(FontStyle.Bold)) + style = FontStyle.Bold; + else if (ff.IsStyleAvailable(FontStyle.Italic)) + style = FontStyle.Italic; + else if (ff.IsStyleAvailable(FontStyle.Underline)) + style = FontStyle.Underline; + else if (ff.IsStyleAvailable(FontStyle.Strikeout)) + style = FontStyle.Strikeout; + } + int fontsize = 8; + int.TryParse(scriptfontsize.Text, out fontsize); + if (ff.IsStyleAvailable(style)) + scriptfontlabel.Font = new Font(scriptfontname.Text, (float)fontsize, style); + } + } + + #endregion + + #region ================== Controls Panel + + // This updates the used keys info + private void UpdateKeyUsedActions() + { + List usedactions = new List(); + + // Anything selected? + if (listactions.SelectedItems.Count > 0) + { + // Get info + int thiskey = (int)listactions.SelectedItems[0].SubItems[1].Tag; + if (thiskey != 0) + { + // Find actions with same key + foreach (ListViewItem item in listactions.Items) + { + // Don't count the selected action + if (item != listactions.SelectedItems[0]) + { + Actions.Action a = General.Actions[item.Name]; + int akey = (int)item.SubItems[1].Tag; + + // Check if the key combination matches + if ((thiskey & a.ShortcutMask) == (akey & a.ShortcutMask)) + usedactions.Add(a.Title + " (" + General.Actions.Categories[a.Category] + ")"); + } + } + } + } + + // Update info + if (usedactions.Count == 0) + { + keyusedlabel.Visible = false; + keyusedlist.Visible = false; + keyusedlist.Items.Clear(); + } + else + { + keyusedlist.Items.Clear(); + foreach (string a in usedactions) keyusedlist.Items.Add(a); + keyusedlabel.Visible = true; + keyusedlist.Visible = true; + } + } + + // This fills the list of available controls for the specified action + private void FillControlsList(Actions.Action a) + { + actioncontrol.Items.Clear(); + + // Fill combobox with special controls + if (a.AllowMouse) + { + actioncontrol.Items.Add(new KeyControl(Keys.LButton, "LButton")); + actioncontrol.Items.Add(new KeyControl(Keys.MButton, "MButton")); + actioncontrol.Items.Add(new KeyControl(Keys.RButton, "RButton")); + actioncontrol.Items.Add(new KeyControl(Keys.XButton1, "XButton1")); + actioncontrol.Items.Add(new KeyControl(Keys.XButton2, "XButton2")); + } + if (a.AllowScroll) + { + actioncontrol.Items.Add(new KeyControl(SpecialKeys.MScrollUp, "ScrollUp")); + actioncontrol.Items.Add(new KeyControl(SpecialKeys.MScrollDown, "ScrollDown")); + } + if (a.AllowMouse && !a.DisregardShift) + { + actioncontrol.Items.Add(new KeyControl(Keys.LButton | Keys.Shift, "Shift+LButton")); + actioncontrol.Items.Add(new KeyControl(Keys.MButton | Keys.Shift, "Shift+MButton")); + actioncontrol.Items.Add(new KeyControl(Keys.RButton | Keys.Shift, "Shift+RButton")); + actioncontrol.Items.Add(new KeyControl(Keys.XButton1 | Keys.Shift, "Shift+XButton1")); + actioncontrol.Items.Add(new KeyControl(Keys.XButton2 | Keys.Shift, "Shift+XButton2")); + } + if (a.AllowScroll && !a.DisregardShift) + { + actioncontrol.Items.Add(new KeyControl((int)SpecialKeys.MScrollUp | (int)Keys.Shift, "Shift+ScrollUp")); + actioncontrol.Items.Add(new KeyControl((int)SpecialKeys.MScrollDown | (int)Keys.Shift, "Shift+ScrollDown")); + } + if (a.AllowMouse && !a.DisregardControl) + { + actioncontrol.Items.Add(new KeyControl(Keys.LButton | Keys.Control, "Ctrl+LButton")); + actioncontrol.Items.Add(new KeyControl(Keys.MButton | Keys.Control, "Ctrl+MButton")); + actioncontrol.Items.Add(new KeyControl(Keys.RButton | Keys.Control, "Ctrl+RButton")); + actioncontrol.Items.Add(new KeyControl(Keys.XButton1 | Keys.Control, "Ctrl+XButton1")); + actioncontrol.Items.Add(new KeyControl(Keys.XButton2 | Keys.Control, "Ctrl+XButton2")); + } + if (a.AllowScroll && !a.DisregardControl) + { + actioncontrol.Items.Add(new KeyControl((int)SpecialKeys.MScrollUp | (int)Keys.Control, "Ctrl+ScrollUp")); + actioncontrol.Items.Add(new KeyControl((int)SpecialKeys.MScrollDown | (int)Keys.Control, "Ctrl+ScrollDown")); + } + if (a.AllowMouse && !a.DisregardShift && !a.DisregardControl) + { + actioncontrol.Items.Add(new KeyControl(Keys.LButton | Keys.Shift | Keys.Control, "Ctrl+Shift+LButton")); + actioncontrol.Items.Add(new KeyControl(Keys.MButton | Keys.Shift | Keys.Control, "Ctrl+Shift+MButton")); + actioncontrol.Items.Add(new KeyControl(Keys.RButton | Keys.Shift | Keys.Control, "Ctrl+Shift+RButton")); + actioncontrol.Items.Add(new KeyControl(Keys.XButton1 | Keys.Shift | Keys.Control, "Ctrl+Shift+XButton1")); + actioncontrol.Items.Add(new KeyControl(Keys.XButton2 | Keys.Shift | Keys.Control, "Ctrl+Shift+XButton2")); + } + if (a.AllowScroll && !a.DisregardShift && !a.DisregardControl) + { + actioncontrol.Items.Add(new KeyControl((int)SpecialKeys.MScrollUp | (int)Keys.Shift | (int)Keys.Control, "Ctrl+Shift+ScrollUp")); + actioncontrol.Items.Add(new KeyControl((int)SpecialKeys.MScrollDown | (int)Keys.Shift | (int)Keys.Control, "Ctrl+Shift+ScrollDown")); + } + } + + // Item selected + private void listactions_ItemSelectionChanged(object sender, ListViewItemSelectionChangedEventArgs e) + { + Actions.Action action; + KeyControl keycontrol; + string disregardkeys = ""; + int key; + + // Anything selected? + if (listactions.SelectedItems.Count > 0) + { + // Begin updating + allowapplycontrol = false; + + // Get the selected action + action = General.Actions[listactions.SelectedItems[0].Name]; + key = (int)listactions.SelectedItems[0].SubItems[1].Tag; + disregardshift = action.DisregardShift; + disregardcontrol = action.DisregardControl; + + // Enable panel + actioncontrolpanel.Enabled = true; + actiontitle.Text = action.Title; + actiondescription.Text = action.Description; + actioncontrol.SelectedIndex = -1; + actionkey.Text = ""; + + if (disregardshift && disregardcontrol) + disregardkeys = "Shift and Control"; + else if (disregardshift) + disregardkeys = "Shift"; + else if (disregardcontrol) + disregardkeys = "Control"; + + disregardshiftlabel.Text = disregardshiftlabel.Tag.ToString().Replace("%s", disregardkeys); + disregardshiftlabel.Visible = disregardshift | disregardcontrol; + + // Fill special controls list + FillControlsList(action); + + // See if the key is in the combobox + for (int i = 0; i < actioncontrol.Items.Count; i++) + { + // Select it when the key is found here + keycontrol = (KeyControl)actioncontrol.Items[i]; + if (keycontrol.key == key) actioncontrol.SelectedIndex = i; + } + + // Otherwise display the key in the textbox + if (actioncontrol.SelectedIndex == -1) + actionkey.Text = Actions.Action.GetShortcutKeyDesc(key); + + // Show actions with same key + UpdateKeyUsedActions(); + + // Focus to the input box + actionkey.Focus(); + + // Done + allowapplycontrol = true; + } + } + + // Key released + private void listactions_KeyUp(object sender, KeyEventArgs e) + { + // Nothing selected? + if (listactions.SelectedItems.Count == 0) + { + // Disable panel + actioncontrolpanel.Enabled = false; + actiontitle.Text = "(select an action from the list)"; + actiondescription.Text = ""; + actionkey.Text = ""; + actioncontrol.SelectedIndex = -1; + disregardshiftlabel.Visible = false; + } + + // Show actions with same key + UpdateKeyUsedActions(); + } + + // Mouse released + private void listactions_MouseUp(object sender, MouseEventArgs e) + { + listactions_KeyUp(sender, new KeyEventArgs(Keys.None)); + + // Focus to the input box + actionkey.Focus(); + } + + // Key combination pressed + private void actionkey_KeyDown(object sender, KeyEventArgs e) + { + int key = (int)e.KeyData; + e.SuppressKeyPress = true; + + // Leave when not allowed to update + if (!allowapplycontrol) return; + + // Anything selected? + if (listactions.SelectedItems.Count > 0) + { + // Begin updating + allowapplycontrol = false; + + // Remove modifier keys from the key if needed + if (disregardshift) key &= ~(int)Keys.Shift; + if (disregardcontrol) key &= ~(int)Keys.Control; + + // Deselect anything from the combobox + actioncontrol.SelectedIndex = -1; + + // Apply the key combination + listactions.SelectedItems[0].SubItems[1].Text = Actions.Action.GetShortcutKeyDesc(key); + listactions.SelectedItems[0].SubItems[1].Tag = key; + actionkey.Text = Actions.Action.GetShortcutKeyDesc(key); + + // Show actions with same key + UpdateKeyUsedActions(); + + // Done + allowapplycontrol = true; + } + } + + // Key combination displayed + private void actionkey_TextChanged(object sender, EventArgs e) + { + // Cursor to the end + actionkey.SelectionStart = actionkey.Text.Length; + actionkey.SelectionLength = 0; + } + + // Special key selected + private void actioncontrol_SelectedIndexChanged(object sender, EventArgs e) + { + KeyControl key; + + // Leave when not allowed to update + if (!allowapplycontrol) return; + + // Anything selected? + if ((actioncontrol.SelectedIndex > -1) && (listactions.SelectedItems.Count > 0)) + { + // Begin updating + allowapplycontrol = false; + + // Remove text from textbox + actionkey.Text = ""; + + // Get the key control + key = (KeyControl)actioncontrol.SelectedItem; + + // Apply the key combination + listactions.SelectedItems[0].SubItems[1].Text = Actions.Action.GetShortcutKeyDesc(key.key); + listactions.SelectedItems[0].SubItems[1].Tag = key.key; + + // Show actions with same key + UpdateKeyUsedActions(); + + // Focus to the input box + actionkey.Focus(); + + // Done + allowapplycontrol = true; + } + } + + // Clear clicked + private void actioncontrolclear_Click(object sender, EventArgs e) + { + // Begin updating + allowapplycontrol = false; + + // Clear textbox and combobox + actionkey.Text = ""; + actioncontrol.SelectedIndex = -1; + + // Apply the key combination + listactions.SelectedItems[0].SubItems[1].Text = ""; + listactions.SelectedItems[0].SubItems[1].Tag = (int)0; + + // Show actions with same key + UpdateKeyUsedActions(); + + // Focus to the input box + actionkey.Focus(); + + // Done + allowapplycontrol = true; + } + + #endregion + + #region ================== Colors Panel + + private void imagebrightness_ValueChanged(object sender, EventArgs e) + { + imagebrightnesslabel.Text = "+ " + imagebrightness.Value.ToString() + " y"; + } + + private void doublesidedalpha_ValueChanged(object sender, EventArgs e) + { + int percent = doublesidedalpha.Value * 10; + doublesidedalphalabel.Text = percent.ToString() + "%"; + } + + #endregion + + // Help + private void PreferencesForm_HelpRequested(object sender, HelpEventArgs hlpevent) + { + if (!actionkey.Focused) + { + General.ShowHelp("w_preferences.html"); + hlpevent.Handled = true; + } + } + + /* + // This writes all action help files using a template and some basic info from the actions. + // Also writes actioncontents.txt with all files to be inserted into Contents.hhc. + // Only used during development. Actual button to call this has been removed. + private void gobutton_Click(object sender, EventArgs e) + { + string template = File.ReadAllText(Path.Combine(General.AppPath, "..\\Help\\a_template.html")); + StringBuilder contents = new StringBuilder("\t
        \r\n"); + string filename; + + // Go for all actions + Action[] actions = General.Actions.GetAllActions(); + foreach(Action a in actions) + { + StringBuilder actionhtml = new StringBuilder(template); + actionhtml.Replace("ACTIONTITLE", a.Title); + actionhtml.Replace("ACTIONDESCRIPTION", a.Description); + actionhtml.Replace("ACTIONCATEGORY", General.Actions.Categories[a.Category]); + filename = Path.Combine(General.AppPath, "..\\Help\\a_" + a.Name + ".html"); + File.WriteAllText(filename, actionhtml.ToString()); + + contents.Append("\t\t
      • \r\n"); + contents.Append("\t\t\t\r\n"); + contents.Append("\t\t\t\r\n"); + contents.Append("\t\t\t\r\n"); + } + + contents.Append("\t
      \r\n"); + filename = Path.Combine(General.AppPath, "..\\Help\\actioncontents.txt"); + File.WriteAllText(filename, contents.ToString()); + } + */ + } +} diff --git a/Source/Core/Windows/PreferencesForm.resx b/Source/Core/Windows/PreferencesForm.resx new file mode 100644 index 0000000..a78f4d3 --- /dev/null +++ b/Source/Core/Windows/PreferencesForm.resx @@ -0,0 +1,474 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + True + + + False + + + True + + + False + + + True + + + False + + + True + + + False + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + False + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + \ No newline at end of file diff --git a/Source/Core/Windows/ResourceOptionsForm.Designer.cs b/Source/Core/Windows/ResourceOptionsForm.Designer.cs new file mode 100644 index 0000000..95c352d --- /dev/null +++ b/Source/Core/Windows/ResourceOptionsForm.Designer.cs @@ -0,0 +1,421 @@ +namespace CodeImp.DoomBuilder.Windows +{ + partial class ResourceOptionsForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.Windows.Forms.Label label1; + System.Windows.Forms.Label label2; + System.Windows.Forms.Label label3; + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(ResourceOptionsForm)); + this.tabs = new System.Windows.Forms.TabControl(); + this.wadfiletab = new System.Windows.Forms.TabPage(); + this.label6 = new System.Windows.Forms.Label(); + this.strictpatches = new System.Windows.Forms.CheckBox(); + this.browsewad = new System.Windows.Forms.Button(); + this.wadlocation = new System.Windows.Forms.TextBox(); + this.directorytab = new System.Windows.Forms.TabPage(); + this.directorylink = new System.Windows.Forms.LinkLabel(); + this.label5 = new System.Windows.Forms.Label(); + this.dir_flats = new System.Windows.Forms.CheckBox(); + this.dir_textures = new System.Windows.Forms.CheckBox(); + this.browsedir = new System.Windows.Forms.Button(); + this.dirlocation = new System.Windows.Forms.TextBox(); + this.pk3filetab = new System.Windows.Forms.TabPage(); + this.pk3link = new System.Windows.Forms.LinkLabel(); + this.label4 = new System.Windows.Forms.Label(); + this.browsepk3 = new System.Windows.Forms.Button(); + this.pk3location = new System.Windows.Forms.TextBox(); + this.cancel = new System.Windows.Forms.Button(); + this.apply = new System.Windows.Forms.Button(); + this.wadfiledialog = new System.Windows.Forms.OpenFileDialog(); + this.dirdialog = new System.Windows.Forms.FolderBrowserDialog(); + this.pk3filedialog = new System.Windows.Forms.OpenFileDialog(); + this.notfortesting = new System.Windows.Forms.CheckBox(); + label1 = new System.Windows.Forms.Label(); + label2 = new System.Windows.Forms.Label(); + label3 = new System.Windows.Forms.Label(); + this.tabs.SuspendLayout(); + this.wadfiletab.SuspendLayout(); + this.directorytab.SuspendLayout(); + this.pk3filetab.SuspendLayout(); + this.SuspendLayout(); + // + // label1 + // + label1.AutoSize = true; + label1.Location = new System.Drawing.Point(15, 20); + label1.Name = "label1"; + label1.Size = new System.Drawing.Size(104, 14); + label1.TabIndex = 0; + label1.Text = "WAD File Resource:"; + // + // label2 + // + label2.AutoSize = true; + label2.Location = new System.Drawing.Point(15, 20); + label2.Name = "label2"; + label2.Size = new System.Drawing.Size(104, 14); + label2.TabIndex = 3; + label2.Text = "Directory Resource:"; + // + // label3 + // + label3.AutoSize = true; + label3.Location = new System.Drawing.Point(15, 20); + label3.Name = "label3"; + label3.Size = new System.Drawing.Size(98, 14); + label3.TabIndex = 3; + label3.Text = "PK3 File Resource:"; + // + // tabs + // + this.tabs.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.tabs.Controls.Add(this.wadfiletab); + this.tabs.Controls.Add(this.directorytab); + this.tabs.Controls.Add(this.pk3filetab); + this.tabs.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tabs.ItemSize = new System.Drawing.Size(110, 19); + this.tabs.Location = new System.Drawing.Point(9, 11); + this.tabs.Name = "tabs"; + this.tabs.Padding = new System.Drawing.Point(20, 3); + this.tabs.SelectedIndex = 0; + this.tabs.Size = new System.Drawing.Size(369, 242); + this.tabs.TabIndex = 0; + // + // wadfiletab + // + this.wadfiletab.Controls.Add(this.label6); + this.wadfiletab.Controls.Add(this.strictpatches); + this.wadfiletab.Controls.Add(this.browsewad); + this.wadfiletab.Controls.Add(this.wadlocation); + this.wadfiletab.Controls.Add(label1); + this.wadfiletab.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.wadfiletab.Location = new System.Drawing.Point(4, 23); + this.wadfiletab.Name = "wadfiletab"; + this.wadfiletab.Padding = new System.Windows.Forms.Padding(3); + this.wadfiletab.Size = new System.Drawing.Size(361, 215); + this.wadfiletab.TabIndex = 0; + this.wadfiletab.Text = "From WAD File"; + this.wadfiletab.UseVisualStyleBackColor = true; + // + // label6 + // + this.label6.Location = new System.Drawing.Point(14, 109); + this.label6.Name = "label6"; + this.label6.Size = new System.Drawing.Size(344, 58); + this.label6.TabIndex = 8; + this.label6.Text = resources.GetString("label6.Text"); + // + // strictpatches + // + this.strictpatches.AutoSize = true; + this.strictpatches.Location = new System.Drawing.Point(17, 72); + this.strictpatches.Name = "strictpatches"; + this.strictpatches.Size = new System.Drawing.Size(298, 18); + this.strictpatches.TabIndex = 2; + this.strictpatches.Text = "Strictly load patches between P_START and P_END only"; + this.strictpatches.UseVisualStyleBackColor = true; + // + // browsewad + // + this.browsewad.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.browsewad.Image = global::CodeImp.DoomBuilder.Properties.Resources.Folder; + this.browsewad.Location = new System.Drawing.Point(315, 36); + this.browsewad.Name = "browsewad"; + this.browsewad.Padding = new System.Windows.Forms.Padding(0, 0, 1, 3); + this.browsewad.Size = new System.Drawing.Size(30, 23); + this.browsewad.TabIndex = 1; + this.browsewad.Text = " "; + this.browsewad.UseVisualStyleBackColor = true; + this.browsewad.Click += new System.EventHandler(this.browsewad_Click); + // + // wadlocation + // + this.wadlocation.Location = new System.Drawing.Point(17, 37); + this.wadlocation.Name = "wadlocation"; + this.wadlocation.ReadOnly = true; + this.wadlocation.Size = new System.Drawing.Size(292, 20); + this.wadlocation.TabIndex = 0; + // + // directorytab + // + this.directorytab.Controls.Add(this.directorylink); + this.directorytab.Controls.Add(this.label5); + this.directorytab.Controls.Add(this.dir_flats); + this.directorytab.Controls.Add(this.dir_textures); + this.directorytab.Controls.Add(this.browsedir); + this.directorytab.Controls.Add(this.dirlocation); + this.directorytab.Controls.Add(label2); + this.directorytab.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.directorytab.Location = new System.Drawing.Point(4, 23); + this.directorytab.Name = "directorytab"; + this.directorytab.Padding = new System.Windows.Forms.Padding(3); + this.directorytab.Size = new System.Drawing.Size(361, 215); + this.directorytab.TabIndex = 1; + this.directorytab.Text = "From Directory"; + this.directorytab.UseVisualStyleBackColor = true; + // + // directorylink + // + this.directorylink.ActiveLinkColor = System.Drawing.Color.Firebrick; + this.directorylink.AutoSize = true; + this.directorylink.DisabledLinkColor = System.Drawing.SystemColors.GrayText; + this.directorylink.LinkArea = new System.Windows.Forms.LinkArea(0, 55); + this.directorylink.LinkBehavior = System.Windows.Forms.LinkBehavior.NeverUnderline; + this.directorylink.LinkColor = System.Drawing.Color.Firebrick; + this.directorylink.Location = new System.Drawing.Point(14, 184); + this.directorylink.Name = "directorylink"; + this.directorylink.Size = new System.Drawing.Size(311, 14); + this.directorylink.TabIndex = 9; + this.directorylink.TabStop = true; + this.directorylink.Text = "http://www.zdoom.org/wiki/Using_ZIPs_as_WAD_replacement"; + this.directorylink.VisitedLinkColor = System.Drawing.Color.Firebrick; + this.directorylink.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.link_Click); + // + // label5 + // + this.label5.Location = new System.Drawing.Point(14, 135); + this.label5.Name = "label5"; + this.label5.Size = new System.Drawing.Size(341, 49); + this.label5.TabIndex = 8; + this.label5.Text = "The directory may use the ZDoom PK3 directory structure, or you can choose to use" + + " the options above to load texture or flat images from the directory root."; + // + // dir_flats + // + this.dir_flats.AutoSize = true; + this.dir_flats.Location = new System.Drawing.Point(17, 98); + this.dir_flats.Name = "dir_flats"; + this.dir_flats.Size = new System.Drawing.Size(205, 18); + this.dir_flats.TabIndex = 3; + this.dir_flats.Text = "Load images in directory root as flats"; + this.dir_flats.UseVisualStyleBackColor = true; + // + // dir_textures + // + this.dir_textures.AutoSize = true; + this.dir_textures.Location = new System.Drawing.Point(17, 72); + this.dir_textures.Name = "dir_textures"; + this.dir_textures.Size = new System.Drawing.Size(224, 18); + this.dir_textures.TabIndex = 2; + this.dir_textures.Text = "Load images in directory root as textures"; + this.dir_textures.UseVisualStyleBackColor = true; + // + // browsedir + // + this.browsedir.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.browsedir.Image = global::CodeImp.DoomBuilder.Properties.Resources.Folder; + this.browsedir.Location = new System.Drawing.Point(315, 36); + this.browsedir.Name = "browsedir"; + this.browsedir.Padding = new System.Windows.Forms.Padding(0, 0, 1, 3); + this.browsedir.Size = new System.Drawing.Size(30, 23); + this.browsedir.TabIndex = 1; + this.browsedir.UseVisualStyleBackColor = true; + this.browsedir.Click += new System.EventHandler(this.browsedir_Click); + // + // dirlocation + // + this.dirlocation.BackColor = System.Drawing.SystemColors.Control; + this.dirlocation.Location = new System.Drawing.Point(17, 37); + this.dirlocation.Name = "dirlocation"; + this.dirlocation.ReadOnly = true; + this.dirlocation.Size = new System.Drawing.Size(292, 20); + this.dirlocation.TabIndex = 0; + // + // pk3filetab + // + this.pk3filetab.Controls.Add(this.pk3link); + this.pk3filetab.Controls.Add(this.label4); + this.pk3filetab.Controls.Add(this.browsepk3); + this.pk3filetab.Controls.Add(this.pk3location); + this.pk3filetab.Controls.Add(label3); + this.pk3filetab.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.pk3filetab.Location = new System.Drawing.Point(4, 23); + this.pk3filetab.Name = "pk3filetab"; + this.pk3filetab.Size = new System.Drawing.Size(361, 215); + this.pk3filetab.TabIndex = 2; + this.pk3filetab.Text = "From PK3 File"; + this.pk3filetab.UseVisualStyleBackColor = true; + // + // pk3link + // + this.pk3link.ActiveLinkColor = System.Drawing.Color.Firebrick; + this.pk3link.AutoSize = true; + this.pk3link.DisabledLinkColor = System.Drawing.SystemColors.GrayText; + this.pk3link.LinkArea = new System.Windows.Forms.LinkArea(0, 55); + this.pk3link.LinkBehavior = System.Windows.Forms.LinkBehavior.NeverUnderline; + this.pk3link.LinkColor = System.Drawing.Color.Firebrick; + this.pk3link.Location = new System.Drawing.Point(14, 111); + this.pk3link.Name = "pk3link"; + this.pk3link.Size = new System.Drawing.Size(311, 14); + this.pk3link.TabIndex = 7; + this.pk3link.TabStop = true; + this.pk3link.Text = "http://www.zdoom.org/wiki/Using_ZIPs_as_WAD_replacement"; + this.pk3link.VisitedLinkColor = System.Drawing.Color.Firebrick; + this.pk3link.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.link_Click); + // + // label4 + // + this.label4.Location = new System.Drawing.Point(15, 83); + this.label4.Name = "label4"; + this.label4.Size = new System.Drawing.Size(343, 28); + this.label4.TabIndex = 6; + this.label4.Text = "The PK3 file is expected to use the ZDoom PK3 directory structure."; + // + // browsepk3 + // + this.browsepk3.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.browsepk3.Image = global::CodeImp.DoomBuilder.Properties.Resources.Folder; + this.browsepk3.Location = new System.Drawing.Point(315, 36); + this.browsepk3.Name = "browsepk3"; + this.browsepk3.Padding = new System.Windows.Forms.Padding(0, 0, 1, 3); + this.browsepk3.Size = new System.Drawing.Size(30, 23); + this.browsepk3.TabIndex = 1; + this.browsepk3.UseVisualStyleBackColor = true; + this.browsepk3.Click += new System.EventHandler(this.browsepk3_Click); + // + // pk3location + // + this.pk3location.Location = new System.Drawing.Point(17, 37); + this.pk3location.Name = "pk3location"; + this.pk3location.ReadOnly = true; + this.pk3location.Size = new System.Drawing.Size(292, 20); + this.pk3location.TabIndex = 0; + // + // cancel + // + this.cancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.cancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.cancel.Location = new System.Drawing.Point(266, 306); + this.cancel.Name = "cancel"; + this.cancel.Size = new System.Drawing.Size(112, 25); + this.cancel.TabIndex = 2; + this.cancel.Text = "Cancel"; + this.cancel.UseVisualStyleBackColor = true; + this.cancel.Click += new System.EventHandler(this.cancel_Click); + // + // apply + // + this.apply.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.apply.Location = new System.Drawing.Point(148, 306); + this.apply.Name = "apply"; + this.apply.Size = new System.Drawing.Size(112, 25); + this.apply.TabIndex = 1; + this.apply.Text = "OK"; + this.apply.UseVisualStyleBackColor = true; + this.apply.Click += new System.EventHandler(this.apply_Click); + // + // wadfiledialog + // + this.wadfiledialog.Filter = "Doom WAD Files (*.wad)|*.wad"; + this.wadfiledialog.Title = "Browse WAD File"; + // + // dirdialog + // + this.dirdialog.Description = "Please select a directory from which to load images when editing your map..."; + // + // pk3filedialog + // + this.pk3filedialog.Filter = "Doom PK3 Files (*.pk3)|*.pk3"; + this.pk3filedialog.Title = "Browse PK3 File"; + // + // notfortesting + // + this.notfortesting.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.notfortesting.AutoSize = true; + this.notfortesting.Location = new System.Drawing.Point(12, 262); + this.notfortesting.Name = "notfortesting"; + this.notfortesting.Size = new System.Drawing.Size(249, 18); + this.notfortesting.TabIndex = 3; + this.notfortesting.Text = "Exclude this resource from testing parameters"; + this.notfortesting.UseVisualStyleBackColor = true; + // + // ResourceOptionsForm + // + this.AcceptButton = this.apply; + this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; + this.CancelButton = this.cancel; + this.ClientSize = new System.Drawing.Size(386, 340); + this.Controls.Add(this.notfortesting); + this.Controls.Add(this.cancel); + this.Controls.Add(this.apply); + this.Controls.Add(this.tabs); + this.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "ResourceOptionsForm"; + this.Opacity = 0; + this.ShowIcon = false; + this.ShowInTaskbar = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Resource Options"; + this.HelpRequested += new System.Windows.Forms.HelpEventHandler(this.ResourceOptionsForm_HelpRequested); + this.tabs.ResumeLayout(false); + this.wadfiletab.ResumeLayout(false); + this.wadfiletab.PerformLayout(); + this.directorytab.ResumeLayout(false); + this.directorytab.PerformLayout(); + this.pk3filetab.ResumeLayout(false); + this.pk3filetab.PerformLayout(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.TabControl tabs; + private System.Windows.Forms.TabPage wadfiletab; + private System.Windows.Forms.TabPage directorytab; + private System.Windows.Forms.Button cancel; + private System.Windows.Forms.Button apply; + private System.Windows.Forms.TextBox wadlocation; + private System.Windows.Forms.Button browsewad; + private System.Windows.Forms.Button browsedir; + private System.Windows.Forms.TextBox dirlocation; + private System.Windows.Forms.CheckBox dir_flats; + private System.Windows.Forms.CheckBox dir_textures; + private System.Windows.Forms.OpenFileDialog wadfiledialog; + private System.Windows.Forms.FolderBrowserDialog dirdialog; + private System.Windows.Forms.TabPage pk3filetab; + private System.Windows.Forms.Button browsepk3; + private System.Windows.Forms.TextBox pk3location; + private System.Windows.Forms.OpenFileDialog pk3filedialog; + private System.Windows.Forms.LinkLabel pk3link; + private System.Windows.Forms.Label label4; + private System.Windows.Forms.LinkLabel directorylink; + private System.Windows.Forms.Label label5; + private System.Windows.Forms.CheckBox strictpatches; + private System.Windows.Forms.Label label6; + private System.Windows.Forms.CheckBox notfortesting; + } +} \ No newline at end of file diff --git a/Source/Core/Windows/ResourceOptionsForm.cs b/Source/Core/Windows/ResourceOptionsForm.cs new file mode 100644 index 0000000..dff2d4c --- /dev/null +++ b/Source/Core/Windows/ResourceOptionsForm.cs @@ -0,0 +1,219 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Text; +using System.Windows.Forms; +using CodeImp.DoomBuilder.Map; +using System.IO; +using CodeImp.DoomBuilder.Data; +using CodeImp.DoomBuilder.Controls; +using CodeImp.DoomBuilder.IO; + +#endregion + +namespace CodeImp.DoomBuilder.Windows +{ + internal partial class ResourceOptionsForm : DelayedForm + { + // Variables + private DataLocation res; + + // Properties + public DataLocation ResourceLocation { get { return res; } } + + // Constructor + public ResourceOptionsForm(DataLocation settings, string caption) + { + // Initialize + InitializeComponent(); + + // Set caption + this.Text = caption; + + // Apply settings from ResourceLocation + this.res = settings; + switch (res.type) + { + // Setup for WAD File + case DataLocation.RESOURCE_WAD: + wadlocation.Text = res.location; + strictpatches.Checked = res.option1; + break; + + // Setup for Directory + case DataLocation.RESOURCE_DIRECTORY: + dirlocation.Text = res.location; + dir_textures.Checked = res.option1; + dir_flats.Checked = res.option2; + break; + + // Setup for PK3 File + case DataLocation.RESOURCE_PK3: + pk3location.Text = res.location; + break; + } + + // Select appropriate tab + tabs.SelectedIndex = res.type; + + // Checkbox + notfortesting.Checked = res.notfortesting; + } + + // OK clicked + private void apply_Click(object sender, EventArgs e) + { + // Apply settings to ResourceLocation + switch (tabs.SelectedIndex) + { + // Setup WAD File + case DataLocation.RESOURCE_WAD: + + // Check if file is specified + if ((wadlocation.Text.Length == 0) || + (!File.Exists(wadlocation.Text))) + { + // No valid wad file specified + MessageBox.Show(this, "Please select a valid WAD File resource.", Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Warning); + } + else + { + // Apply settings + res.type = DataLocation.RESOURCE_WAD; + res.location = wadlocation.Text; + res.option1 = strictpatches.Checked; + res.option2 = false; + res.notfortesting = notfortesting.Checked; + + // Done + this.DialogResult = DialogResult.OK; + this.Close(); + } + break; + + // Setup Directory + case DataLocation.RESOURCE_DIRECTORY: + + // Check if directory is specified + if ((dirlocation.Text.Length == 0) || + (!Directory.Exists(dirlocation.Text))) + { + // No valid directory specified + MessageBox.Show(this, "Please select a valid directory resource.", Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Warning); + } + else + { + // Apply settings + res.type = DataLocation.RESOURCE_DIRECTORY; + res.location = dirlocation.Text; + res.option1 = dir_textures.Checked; + res.option2 = dir_flats.Checked; + res.notfortesting = notfortesting.Checked; + + // Done + this.DialogResult = DialogResult.OK; + this.Close(); + } + break; + + // Setup PK3 File + case DataLocation.RESOURCE_PK3: + + // Check if file is specified + if ((pk3location.Text.Length == 0) || + (!File.Exists(pk3location.Text))) + { + // No valid pk3 file specified + MessageBox.Show(this, "Please select a valid PK3 File resource.", Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Warning); + } + else + { + // Apply settings + res.type = DataLocation.RESOURCE_PK3; + res.location = pk3location.Text; + res.option1 = false; + res.option2 = false; + res.notfortesting = notfortesting.Checked; + + // Done + this.DialogResult = DialogResult.OK; + this.Close(); + } + break; + } + } + + // Cancel clicked + private void cancel_Click(object sender, EventArgs e) + { + // Just hide + this.DialogResult = DialogResult.Cancel; + this.Close(); + } + + // Browse WAD File clicked + private void browsewad_Click(object sender, EventArgs e) + { + // Browse for WAD File + if (wadfiledialog.ShowDialog(this) == DialogResult.OK) + { + // Use this file + wadlocation.Text = wadfiledialog.FileName; + } + } + + // Browse Directory clicked + private void browsedir_Click(object sender, EventArgs e) + { + // Browse for Directory + if (dirdialog.ShowDialog(this) == DialogResult.OK) + { + // Use this directory + dirlocation.Text = dirdialog.SelectedPath; + } + } + + // Browse PK3 File clicked + private void browsepk3_Click(object sender, EventArgs e) + { + // Browse for PK3 File + if (pk3filedialog.ShowDialog(this) == DialogResult.OK) + { + // Use this file + pk3location.Text = pk3filedialog.FileName; + } + } + + // Link clicked + private void link_Click(object sender, LinkLabelLinkClickedEventArgs e) + { + General.OpenWebsite((sender as LinkLabel).Text); + } + + // Help + private void ResourceOptionsForm_HelpRequested(object sender, HelpEventArgs hlpevent) + { + General.ShowHelp("w_resourceoptions.html"); + hlpevent.Handled = true; + } + } +} \ No newline at end of file diff --git a/Source/Core/Windows/ResourceOptionsForm.resx b/Source/Core/Windows/ResourceOptionsForm.resx new file mode 100644 index 0000000..f98ca2a --- /dev/null +++ b/Source/Core/Windows/ResourceOptionsForm.resx @@ -0,0 +1,216 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + True + + + False + + + True + + + False + + + True + + + False + + + True + + + True + + + True + + + Use the option above to enforce strictly loading texture patches from between P_START and P_END marker lumps only. This can solve lump name conflicts, but old WAD files do not always adhere to this rule. + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + 17, 17 + + + 133, 17 + + + 227, 17 + + + True + + + True + + \ No newline at end of file diff --git a/Source/Core/Windows/ScriptEditorForm.Designer.cs b/Source/Core/Windows/ScriptEditorForm.Designer.cs new file mode 100644 index 0000000..2401b7e --- /dev/null +++ b/Source/Core/Windows/ScriptEditorForm.Designer.cs @@ -0,0 +1,71 @@ +namespace CodeImp.DoomBuilder.Windows +{ + partial class ScriptEditorForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(ScriptEditorForm)); + this.editor = new CodeImp.DoomBuilder.Controls.ScriptEditorPanel(); + this.SuspendLayout(); + // + // editor + // + this.editor.BackColor = System.Drawing.SystemColors.Control; + this.editor.Dock = System.Windows.Forms.DockStyle.Fill; + this.editor.Location = new System.Drawing.Point(0, 0); + this.editor.Name = "editor"; + this.editor.Size = new System.Drawing.Size(729, 578); + this.editor.TabIndex = 0; + this.editor.TabStop = false; + // + // ScriptEditorForm + // + this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; + this.ClientSize = new System.Drawing.Size(729, 578); + this.Controls.Add(this.editor); + this.DoubleBuffered = true; + this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); + this.KeyPreview = true; + this.Name = "ScriptEditorForm"; + this.StartPosition = System.Windows.Forms.FormStartPosition.Manual; + this.Text = "Doom Builder Script Editor"; + this.Load += new System.EventHandler(this.ScriptEditorForm_Load); + this.Shown += new System.EventHandler(this.ScriptEditorForm_Shown); + this.Move += new System.EventHandler(this.ScriptEditorForm_Move); + this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.ScriptEditorForm_FormClosing); + this.HelpRequested += new System.Windows.Forms.HelpEventHandler(this.ScriptEditorForm_HelpRequested); + this.ResizeEnd += new System.EventHandler(this.ScriptEditorForm_ResizeEnd); + this.ResumeLayout(false); + + } + + #endregion + + private CodeImp.DoomBuilder.Controls.ScriptEditorPanel editor; + } +} \ No newline at end of file diff --git a/Source/Core/Windows/ScriptEditorForm.cs b/Source/Core/Windows/ScriptEditorForm.cs new file mode 100644 index 0000000..7a84af5 --- /dev/null +++ b/Source/Core/Windows/ScriptEditorForm.cs @@ -0,0 +1,194 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Text; +using System.Windows.Forms; +using CodeImp.DoomBuilder.Map; +using System.IO; +using CodeImp.DoomBuilder.Data; +using CodeImp.DoomBuilder.Controls; + +#endregion + +namespace CodeImp.DoomBuilder.Windows +{ + internal partial class ScriptEditorForm : Form + { + #region ================== Variables + + // Position/size + private Point lastposition; + private Size lastsize; + + // Closing? + private bool appclose; + + #endregion + + #region ================== Properties + + public ScriptEditorPanel Editor { get { return editor; } } + + #endregion + + #region ================== Constructor + + // Constructor + public ScriptEditorForm() + { + InitializeComponent(); + editor.Initialize(); + } + + #endregion + + #region ================== Methods + + // This asks to save files and returns the result + // Also does implicit saves + // Returns false when cancelled by the user + public bool AskSaveAll() + { + // Implicit-save the script lumps + editor.ImplicitSave(); + + // Save other scripts + return editor.AskSaveAll(); + } + + // Close the window + new public void Close() + { + appclose = true; + base.Close(); + } + + #endregion + + #region ================== Events + + // Window is loaded + private void ScriptEditorForm_Load(object sender, EventArgs e) + { + this.SuspendLayout(); + this.Location = new Point(General.Settings.ReadSetting("scriptswindow.positionx", this.Location.X), + General.Settings.ReadSetting("scriptswindow.positiony", this.Location.Y)); + this.Size = new Size(General.Settings.ReadSetting("scriptswindow.sizewidth", this.Size.Width), + General.Settings.ReadSetting("scriptswindow.sizeheight", this.Size.Height)); + this.WindowState = (FormWindowState)General.Settings.ReadSetting("scriptswindow.windowstate", (int)FormWindowState.Normal); + this.ResumeLayout(true); + + // Normal windowstate? + if (this.WindowState == FormWindowState.Normal) + { + // Keep last position and size + lastposition = this.Location; + lastsize = this.Size; + } + + // Apply panel settings + editor.ApplySettings(); + } + + // Window is shown + private void ScriptEditorForm_Shown(object sender, EventArgs e) + { + // Focus to script editor + editor.ForceFocus(); + } + + // Window is closing + private void ScriptEditorForm_FormClosing(object sender, FormClosingEventArgs e) + { + int windowstate; + + // Determine window state to save + if (this.WindowState != FormWindowState.Minimized) + windowstate = (int)this.WindowState; + else + windowstate = (int)FormWindowState.Normal; + + // Save window settings + General.Settings.WriteSetting("scriptswindow.positionx", lastposition.X); + General.Settings.WriteSetting("scriptswindow.positiony", lastposition.Y); + General.Settings.WriteSetting("scriptswindow.sizewidth", lastsize.Width); + General.Settings.WriteSetting("scriptswindow.sizeheight", lastsize.Height); + General.Settings.WriteSetting("scriptswindow.windowstate", windowstate); + editor.SaveSettings(); + + // Only when closed by the user + if (!appclose) + { + // Remember if scipts are changed + General.Map.ApplyScriptChanged(); + + // Ask to save scripts + if (AskSaveAll()) + { + // Let the general call close the editor + General.Map.CloseScriptEditor(true); + } + else + { + // Cancel + e.Cancel = true; + } + } + + // Not cancelling? + if (!e.Cancel) editor.OnClose(); + } + + // Window resized + private void ScriptEditorForm_ResizeEnd(object sender, EventArgs e) + { + // Normal windowstate? + if (this.WindowState == FormWindowState.Normal) + { + // Keep last position and size + lastposition = this.Location; + lastsize = this.Size; + } + } + + // Window moved + private void ScriptEditorForm_Move(object sender, EventArgs e) + { + // Normal windowstate? + if (this.WindowState == FormWindowState.Normal) + { + // Keep last position and size + lastposition = this.Location; + lastsize = this.Size; + } + } + + // Help + private void ScriptEditorForm_HelpRequested(object sender, HelpEventArgs hlpevent) + { + General.ShowHelp("w_scripteditor.html"); + hlpevent.Handled = true; + } + + #endregion + } +} \ No newline at end of file diff --git a/Source/Core/Windows/ScriptEditorForm.resx b/Source/Core/Windows/ScriptEditorForm.resx new file mode 100644 index 0000000..81d185b --- /dev/null +++ b/Source/Core/Windows/ScriptEditorForm.resx @@ -0,0 +1,145 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + + AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AACQWzEBkFsxRJBbMWiQWzGBkFsxnpBbMbqQWzHYkFsx9ZVhOf+aaUH/o3RP/62CX/+QWzG4kFsxCgAA + AAAAAAAAlF8zKriQbf/WuqP/38az/+fUw//u39P/9eri//v07//9+vb///79//vr3//77+b/wZ2A/5Rf + MzAAAAAAAAAAAJlkNknHpIX////////////////////////+/P/++/f//vfx//728f/krob/+ujb/86v + lf+ZZDZLAAAAAAAAAACeaTkdnmk57bSFWf/ZpXv/2J5v/9ebav/YlmP/1pJd/9SPWf/Tjlf/4p1p//rj + 0f/Yu6L/nmk5YQAAAAAAAAAApG88AaRvPHLVroz//fDl//bGof/2zqz/+NS0//bYu//127//997D//rk + zP/99ez/4s66/6RvPH0AAAAAAAAAAAAAAACqdUAGt4ZW//7+/f/ozrT/z7ae/861n//OtZ//zreg/9a7 + pP/63MP//eve/+zczf+qdUCgAAAAAAAAAAAAAAAAsXtDA7mGUv/+/Pn/99q9/+3Rtf/u0rf/8tS8//LV + u//z173/+dzC//vn1P/17eL/sXtDywAAAAAAAAAAAAAAAAAAAAC5hUv//vv3/+/Tuf/QuaD/z7ig/8+3 + of/PuKD/z7ig//HWvP/74sv//Pn1/7eCR/cAAAAAAAAAAAAAAAAAAAAAvYhL8/z28P/538f/8dW3//LV + uP/y1Lr/8ta8//LWu//43MP/++DJ///8+v/BjVP/AAAAAAAAAAAAAAAAAAAAAMSOTrr159j/+eTR/9K4 + n//SuZ//07mh/9O7o//Tu6P/8ta+//vhyP///fv/yJRX/8SOTgEAAAAAAAAAAAAAAADKlFKJ8NnB//vt + 4f/42b//+NvB//fcw//43sX/99/H//ffyv/65dD///79/8uPWv/KlFLjypRSEgAAAAAAAAAA0JpVae3Q + sv//9vD/6NG7/8+7qP/Pu6v/z76u/8/AsP/Xx7j//Orb/////f/SnXH/7tnB/9CaVc0AAAAAAAAAANWf + WFPryqX///37//vn0//66NX/+ufY//vr3f/77uD//PDj//zw5P//////4KBw///7+f/fuIf/AAAAAAAA + AADapFtE68aa///////87+L//fDn//3x6//99e7//fjx//369////Pr///////779//02sD/2qRb1gAA + AAAAAAAA3qhdLurAjP///////////////////////fn0//vz6v/469n/+ObT//Xfxv/py6b/3qhd3N6o + XSEAAAAAAAAAAOKsXwvirF+Z6ryB/+i3d//msm3/5LBo/+KsX+HirF/K4qxfqOKsX6PirF+J4qxfbuKs + XxYAAAAAAAOsQQADrEEAA6xBAAOsQQADrEGAA6xBgAOsQcADrEHAA6xBwAGsQcAArEHAAKxBwACsQcAA + rEHAAKxBwAGsQQ== + + + \ No newline at end of file diff --git a/Source/Core/Windows/ScriptFindReplaceForm.Designer.cs b/Source/Core/Windows/ScriptFindReplaceForm.Designer.cs new file mode 100644 index 0000000..41360ac --- /dev/null +++ b/Source/Core/Windows/ScriptFindReplaceForm.Designer.cs @@ -0,0 +1,176 @@ +namespace CodeImp.DoomBuilder.Windows +{ + partial class ScriptFindReplaceForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.label1 = new System.Windows.Forms.Label(); + this.findtext = new System.Windows.Forms.TextBox(); + this.casesensitive = new System.Windows.Forms.CheckBox(); + this.wordonly = new System.Windows.Forms.CheckBox(); + this.replacetext = new System.Windows.Forms.TextBox(); + this.label2 = new System.Windows.Forms.Label(); + this.findnextbutton = new System.Windows.Forms.Button(); + this.replaceallbutton = new System.Windows.Forms.Button(); + this.closebutton = new System.Windows.Forms.Button(); + this.replacebutton = new System.Windows.Forms.Button(); + this.SuspendLayout(); + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(29, 23); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(58, 14); + this.label1.TabIndex = 0; + this.label1.Text = "Find what:"; + // + // findtext + // + this.findtext.Location = new System.Drawing.Point(93, 20); + this.findtext.Name = "findtext"; + this.findtext.Size = new System.Drawing.Size(152, 20); + this.findtext.TabIndex = 0; + // + // casesensitive + // + this.casesensitive.AutoSize = true; + this.casesensitive.Location = new System.Drawing.Point(93, 84); + this.casesensitive.Name = "casesensitive"; + this.casesensitive.Size = new System.Drawing.Size(97, 18); + this.casesensitive.TabIndex = 2; + this.casesensitive.Text = "Case sensitive"; + this.casesensitive.UseVisualStyleBackColor = true; + // + // wordonly + // + this.wordonly.AutoSize = true; + this.wordonly.Location = new System.Drawing.Point(93, 108); + this.wordonly.Name = "wordonly"; + this.wordonly.Size = new System.Drawing.Size(108, 18); + this.wordonly.TabIndex = 3; + this.wordonly.Text = "Whole word only"; + this.wordonly.UseVisualStyleBackColor = true; + // + // replacetext + // + this.replacetext.Location = new System.Drawing.Point(93, 51); + this.replacetext.Name = "replacetext"; + this.replacetext.Size = new System.Drawing.Size(152, 20); + this.replacetext.TabIndex = 1; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(14, 54); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(73, 14); + this.label2.TabIndex = 6; + this.label2.Text = "Replace with:"; + // + // findnextbutton + // + this.findnextbutton.Location = new System.Drawing.Point(277, 18); + this.findnextbutton.Name = "findnextbutton"; + this.findnextbutton.Size = new System.Drawing.Size(98, 25); + this.findnextbutton.TabIndex = 4; + this.findnextbutton.Text = "Find Next"; + this.findnextbutton.UseVisualStyleBackColor = true; + this.findnextbutton.Click += new System.EventHandler(this.findnextbutton_Click); + // + // replaceallbutton + // + this.replaceallbutton.Location = new System.Drawing.Point(277, 80); + this.replaceallbutton.Name = "replaceallbutton"; + this.replaceallbutton.Size = new System.Drawing.Size(98, 25); + this.replaceallbutton.TabIndex = 6; + this.replaceallbutton.Text = "Replace All"; + this.replaceallbutton.UseVisualStyleBackColor = true; + this.replaceallbutton.Click += new System.EventHandler(this.replaceallbutton_Click); + // + // closebutton + // + this.closebutton.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.closebutton.Location = new System.Drawing.Point(277, 127); + this.closebutton.Name = "closebutton"; + this.closebutton.Size = new System.Drawing.Size(98, 25); + this.closebutton.TabIndex = 7; + this.closebutton.Text = "Close"; + this.closebutton.UseVisualStyleBackColor = true; + this.closebutton.Click += new System.EventHandler(this.closebutton_Click); + // + // replacebutton + // + this.replacebutton.Location = new System.Drawing.Point(277, 49); + this.replacebutton.Name = "replacebutton"; + this.replacebutton.Size = new System.Drawing.Size(98, 25); + this.replacebutton.TabIndex = 5; + this.replacebutton.Text = "Replace"; + this.replacebutton.UseVisualStyleBackColor = true; + this.replacebutton.Click += new System.EventHandler(this.replacebutton_Click); + // + // ScriptFindReplaceForm + // + this.AcceptButton = this.findnextbutton; + this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; + this.CancelButton = this.closebutton; + this.ClientSize = new System.Drawing.Size(387, 163); + this.Controls.Add(this.replacebutton); + this.Controls.Add(this.closebutton); + this.Controls.Add(this.replaceallbutton); + this.Controls.Add(this.findnextbutton); + this.Controls.Add(this.label2); + this.Controls.Add(this.replacetext); + this.Controls.Add(this.wordonly); + this.Controls.Add(this.casesensitive); + this.Controls.Add(this.findtext); + this.Controls.Add(this.label1); + this.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedToolWindow; + this.Name = "ScriptFindReplaceForm"; + this.Text = "Find and Replace"; + this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.ScriptFindReplaceForm_FormClosing); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Label label1; + private System.Windows.Forms.TextBox findtext; + private System.Windows.Forms.CheckBox casesensitive; + private System.Windows.Forms.CheckBox wordonly; + private System.Windows.Forms.TextBox replacetext; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.Button findnextbutton; + private System.Windows.Forms.Button replaceallbutton; + private System.Windows.Forms.Button closebutton; + private System.Windows.Forms.Button replacebutton; + } +} \ No newline at end of file diff --git a/Source/Core/Windows/ScriptFindReplaceForm.cs b/Source/Core/Windows/ScriptFindReplaceForm.cs new file mode 100644 index 0000000..2aa4b4a --- /dev/null +++ b/Source/Core/Windows/ScriptFindReplaceForm.cs @@ -0,0 +1,125 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Text; +using System.Windows.Forms; +using CodeImp.DoomBuilder.Map; +using System.IO; +using CodeImp.DoomBuilder.Data; +using CodeImp.DoomBuilder.Controls; + +#endregion + +namespace CodeImp.DoomBuilder.Windows +{ + public partial class ScriptFindReplaceForm : DelayedForm + { + #region ================== Variables + + private bool appclose; + + #endregion + + #region ================== Properties + + #endregion + + #region ================== Constructor + + // Constructor + public ScriptFindReplaceForm() + { + InitializeComponent(); + } + + #endregion + + #region ================== Methods + + // This makes the Find & Replace options + private FindReplaceOptions MakeOptions() + { + FindReplaceOptions options = new FindReplaceOptions(); + options.FindText = findtext.Text; + options.CaseSensitive = casesensitive.Checked; + options.WholeWord = wordonly.Checked; + options.ReplaceWith = replacetext.Text; + return options; + } + + // Close the window + new public void Close() + { + appclose = true; + base.Close(); + } + + // This sets the text to find + public void SetFindText(string text) + { + findtext.Text = text; + findtext.SelectAll(); + } + + #endregion + + #region ================== Events + + // Form is closing + private void ScriptFindReplaceForm_FormClosing(object sender, FormClosingEventArgs e) + { + if (!appclose) + { + General.Map.ScriptEditor.Editor.CloseFindReplace(true); + } + } + + // Find Next + private void findnextbutton_Click(object sender, EventArgs e) + { + General.Map.ScriptEditor.Editor.FindNext(MakeOptions()); + } + + // Replace + private void replacebutton_Click(object sender, EventArgs e) + { + FindReplaceOptions options = MakeOptions(); + + General.Map.ScriptEditor.Editor.Replace(options); + General.Map.ScriptEditor.Editor.FindNext(options); + } + + // Replace All + private void replaceallbutton_Click(object sender, EventArgs e) + { + General.Map.ScriptEditor.Editor.ReplaceAll(MakeOptions()); + } + + // Close + private void closebutton_Click(object sender, EventArgs e) + { + General.Map.ScriptEditor.Editor.CloseFindReplace(false); + } + + #endregion + } +} \ No newline at end of file diff --git a/Source/Core/Windows/ScriptFindReplaceForm.resx b/Source/Core/Windows/ScriptFindReplaceForm.resx new file mode 100644 index 0000000..fd45dde --- /dev/null +++ b/Source/Core/Windows/ScriptFindReplaceForm.resx @@ -0,0 +1,153 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + \ No newline at end of file diff --git a/Source/Core/Windows/SectorEditForm.Designer.cs b/Source/Core/Windows/SectorEditForm.Designer.cs new file mode 100644 index 0000000..3179b65 --- /dev/null +++ b/Source/Core/Windows/SectorEditForm.Designer.cs @@ -0,0 +1,681 @@ +namespace CodeImp.DoomBuilder.Windows +{ + partial class SectorEditForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.Windows.Forms.Label label1; + System.Windows.Forms.Label label3; + System.Windows.Forms.Label labelTag; + System.Windows.Forms.Label labelSpecial; + System.Windows.Forms.GroupBox groupfloorceiling; + System.Windows.Forms.Label labelFloorHeight; + System.Windows.Forms.Label label2; + System.Windows.Forms.Label label4; + System.Windows.Forms.Label labelCeilingHeight; + System.Windows.Forms.GroupBox incdecIntensity; + System.Windows.Forms.GroupBox coloredLightingInfo; + this.floorheight = new CodeImp.DoomBuilder.Controls.ButtonsNumericTextbox(); + this.ceilingheight = new CodeImp.DoomBuilder.Controls.ButtonsNumericTextbox(); + this.sectorheight = new System.Windows.Forms.Label(); + this.sectorheightlabel = new System.Windows.Forms.Label(); + this.floortex = new CodeImp.DoomBuilder.Controls.FlatSelectorControl(); + this.ceilingtex = new CodeImp.DoomBuilder.Controls.FlatSelectorControl(); + this.button19 = new System.Windows.Forms.Button(); + this.button20 = new System.Windows.Forms.Button(); + this.button17 = new System.Windows.Forms.Button(); + this.button18 = new System.Windows.Forms.Button(); + this.button5 = new System.Windows.Forms.Button(); + this.button16 = new System.Windows.Forms.Button(); + this.button3 = new System.Windows.Forms.Button(); + this.button4 = new System.Windows.Forms.Button(); + this.button2 = new System.Windows.Forms.Button(); + this.button1 = new System.Windows.Forms.Button(); + this.floorcolor = new CodeImp.DoomBuilder.Controls.ColorControlSector(); + this.lowercolor = new CodeImp.DoomBuilder.Controls.ColorControlSector(); + this.thingcolor = new CodeImp.DoomBuilder.Controls.ColorControlSector(); + this.topcolor = new CodeImp.DoomBuilder.Controls.ColorControlSector(); + this.ceilingcolor = new CodeImp.DoomBuilder.Controls.ColorControlSector(); + this.groupaction = new System.Windows.Forms.GroupBox(); + this.tag = new CodeImp.DoomBuilder.Controls.ButtonsNumericTextbox(); + this.newtag = new System.Windows.Forms.Button(); + this.groupeffect = new System.Windows.Forms.GroupBox(); + this.browseeffect = new System.Windows.Forms.Button(); + this.effect = new CodeImp.DoomBuilder.Controls.ActionSelectorControl(); + this.cancel = new System.Windows.Forms.Button(); + this.apply = new System.Windows.Forms.Button(); + this.sectorproperties = new System.Windows.Forms.Panel(); + this.lightsPanel = new System.Windows.Forms.Panel(); + this.settingsgroup = new System.Windows.Forms.GroupBox(); + this.flags = new CodeImp.DoomBuilder.Controls.CheckboxArrayControl(); + this.flatSelectorControl2 = new CodeImp.DoomBuilder.Controls.FlatSelectorControl(); + this.flatSelectorControl1 = new CodeImp.DoomBuilder.Controls.FlatSelectorControl(); + this.heightpanel3 = new System.Windows.Forms.Panel(); + label1 = new System.Windows.Forms.Label(); + label3 = new System.Windows.Forms.Label(); + labelTag = new System.Windows.Forms.Label(); + labelSpecial = new System.Windows.Forms.Label(); + groupfloorceiling = new System.Windows.Forms.GroupBox(); + labelFloorHeight = new System.Windows.Forms.Label(); + label2 = new System.Windows.Forms.Label(); + label4 = new System.Windows.Forms.Label(); + labelCeilingHeight = new System.Windows.Forms.Label(); + incdecIntensity = new System.Windows.Forms.GroupBox(); + coloredLightingInfo = new System.Windows.Forms.GroupBox(); + groupfloorceiling.SuspendLayout(); + incdecIntensity.SuspendLayout(); + coloredLightingInfo.SuspendLayout(); + this.groupaction.SuspendLayout(); + this.groupeffect.SuspendLayout(); + this.sectorproperties.SuspendLayout(); + this.lightsPanel.SuspendLayout(); + this.settingsgroup.SuspendLayout(); + this.SuspendLayout(); + // + // label1 + // + label1.Location = new System.Drawing.Point(271, 18); + label1.Name = "label1"; + label1.Size = new System.Drawing.Size(83, 16); + label1.TabIndex = 15; + label1.Text = "Floor"; + label1.TextAlign = System.Drawing.ContentAlignment.TopCenter; + // + // label3 + // + label3.Location = new System.Drawing.Point(363, 18); + label3.Name = "label3"; + label3.Size = new System.Drawing.Size(83, 16); + label3.TabIndex = 14; + label3.Text = "Ceiling"; + label3.TextAlign = System.Drawing.ContentAlignment.TopCenter; + // + // labelTag + // + labelTag.AutoSize = true; + labelTag.Location = new System.Drawing.Point(55, 21); + labelTag.Name = "labelTag"; + labelTag.Size = new System.Drawing.Size(27, 14); + labelTag.TabIndex = 9; + labelTag.Text = "Tag:"; + // + // labelSpecial + // + labelSpecial.AutoSize = true; + labelSpecial.Location = new System.Drawing.Point(38, 25); + labelSpecial.Name = "labelSpecial"; + labelSpecial.Size = new System.Drawing.Size(45, 14); + labelSpecial.TabIndex = 0; + labelSpecial.Text = "Special:"; + // + // groupfloorceiling + // + groupfloorceiling.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + groupfloorceiling.Controls.Add(this.floorheight); + groupfloorceiling.Controls.Add(this.ceilingheight); + groupfloorceiling.Controls.Add(this.sectorheight); + groupfloorceiling.Controls.Add(this.sectorheightlabel); + groupfloorceiling.Controls.Add(labelFloorHeight); + groupfloorceiling.Controls.Add(label2); + groupfloorceiling.Controls.Add(label4); + groupfloorceiling.Controls.Add(this.floortex); + groupfloorceiling.Controls.Add(this.ceilingtex); + groupfloorceiling.Controls.Add(labelCeilingHeight); + groupfloorceiling.Location = new System.Drawing.Point(7, 6); + groupfloorceiling.Name = "groupfloorceiling"; + groupfloorceiling.Size = new System.Drawing.Size(436, 143); + groupfloorceiling.TabIndex = 0; + groupfloorceiling.TabStop = false; + groupfloorceiling.Text = "Floor and Ceiling "; + // + // floorheight + // + this.floorheight.AllowDecimal = false; + this.floorheight.AllowNegative = true; + this.floorheight.AllowRelative = true; + this.floorheight.ButtonStep = 8; + this.floorheight.Location = new System.Drawing.Point(112, 67); + this.floorheight.Name = "floorheight"; + this.floorheight.Size = new System.Drawing.Size(88, 24); + this.floorheight.StepValues = null; + this.floorheight.TabIndex = 23; + this.floorheight.WhenTextChanged += new System.EventHandler(this.floorheight_TextChanged); + // + // ceilingheight + // + this.ceilingheight.AllowDecimal = false; + this.ceilingheight.AllowNegative = true; + this.ceilingheight.AllowRelative = true; + this.ceilingheight.ButtonStep = 8; + this.ceilingheight.Location = new System.Drawing.Point(112, 33); + this.ceilingheight.Name = "ceilingheight"; + this.ceilingheight.Size = new System.Drawing.Size(88, 24); + this.ceilingheight.StepValues = null; + this.ceilingheight.TabIndex = 22; + this.ceilingheight.WhenTextChanged += new System.EventHandler(this.ceilingheight_TextChanged); + // + // sectorheight + // + this.sectorheight.AutoSize = true; + this.sectorheight.Location = new System.Drawing.Point(113, 107); + this.sectorheight.Name = "sectorheight"; + this.sectorheight.Size = new System.Drawing.Size(13, 14); + this.sectorheight.TabIndex = 21; + this.sectorheight.Text = "0"; + // + // sectorheightlabel + // + this.sectorheightlabel.AutoSize = true; + this.sectorheightlabel.Location = new System.Drawing.Point(32, 107); + this.sectorheightlabel.Name = "sectorheightlabel"; + this.sectorheightlabel.Size = new System.Drawing.Size(74, 14); + this.sectorheightlabel.TabIndex = 20; + this.sectorheightlabel.Text = "Sector height:"; + // + // labelFloorHeight + // + labelFloorHeight.AutoSize = true; + labelFloorHeight.Location = new System.Drawing.Point(40, 72); + labelFloorHeight.Name = "labelFloorHeight"; + labelFloorHeight.Size = new System.Drawing.Size(66, 14); + labelFloorHeight.TabIndex = 17; + labelFloorHeight.Text = "Floor height:"; + // + // label2 + // + label2.Location = new System.Drawing.Point(237, 11); + label2.Name = "label2"; + label2.Size = new System.Drawing.Size(83, 16); + label2.TabIndex = 15; + label2.Text = "Floor"; + label2.TextAlign = System.Drawing.ContentAlignment.TopCenter; + // + // label4 + // + label4.Location = new System.Drawing.Point(332, 11); + label4.Name = "label4"; + label4.Size = new System.Drawing.Size(83, 16); + label4.TabIndex = 14; + label4.Text = "Ceiling"; + label4.TextAlign = System.Drawing.ContentAlignment.TopCenter; + // + // floortex + // + this.floortex.Location = new System.Drawing.Point(237, 30); + this.floortex.Name = "floortex"; + this.floortex.Size = new System.Drawing.Size(83, 105); + this.floortex.TabIndex = 2; + this.floortex.TextureName = ""; + // + // ceilingtex + // + this.ceilingtex.Location = new System.Drawing.Point(332, 30); + this.ceilingtex.Name = "ceilingtex"; + this.ceilingtex.Size = new System.Drawing.Size(83, 105); + this.ceilingtex.TabIndex = 3; + this.ceilingtex.TextureName = ""; + // + // labelCeilingHeight + // + labelCeilingHeight.AutoSize = true; + labelCeilingHeight.Location = new System.Drawing.Point(33, 38); + labelCeilingHeight.Name = "labelCeilingHeight"; + labelCeilingHeight.Size = new System.Drawing.Size(73, 14); + labelCeilingHeight.TabIndex = 19; + labelCeilingHeight.Text = "Ceiling height:"; + // + // incdecIntensity + // + incdecIntensity.Controls.Add(this.button19); + incdecIntensity.Controls.Add(this.button20); + incdecIntensity.Controls.Add(this.button17); + incdecIntensity.Controls.Add(this.button18); + incdecIntensity.Controls.Add(this.button5); + incdecIntensity.Controls.Add(this.button16); + incdecIntensity.Controls.Add(this.button3); + incdecIntensity.Controls.Add(this.button4); + incdecIntensity.Controls.Add(this.button2); + incdecIntensity.Controls.Add(this.button1); + incdecIntensity.Location = new System.Drawing.Point(324, 7); + incdecIntensity.Name = "incdecIntensity"; + incdecIntensity.Size = new System.Drawing.Size(112, 167); + incdecIntensity.TabIndex = 6; + incdecIntensity.TabStop = false; + incdecIntensity.Text = "Inc/Dec Intensity"; + // + // button19 + // + this.button19.Location = new System.Drawing.Point(59, 135); + this.button19.Name = "button19"; + this.button19.Size = new System.Drawing.Size(42, 20); + this.button19.TabIndex = 61; + this.button19.Text = "-"; + this.button19.UseVisualStyleBackColor = true; + // + // button20 + // + this.button20.Location = new System.Drawing.Point(11, 135); + this.button20.Name = "button20"; + this.button20.Size = new System.Drawing.Size(42, 20); + this.button20.TabIndex = 60; + this.button20.Text = "+"; + this.button20.UseVisualStyleBackColor = true; + // + // button17 + // + this.button17.Location = new System.Drawing.Point(59, 106); + this.button17.Name = "button17"; + this.button17.Size = new System.Drawing.Size(42, 20); + this.button17.TabIndex = 59; + this.button17.Text = "-"; + this.button17.UseVisualStyleBackColor = true; + // + // button18 + // + this.button18.Location = new System.Drawing.Point(11, 106); + this.button18.Name = "button18"; + this.button18.Size = new System.Drawing.Size(42, 20); + this.button18.TabIndex = 58; + this.button18.Text = "+"; + this.button18.UseVisualStyleBackColor = true; + // + // button5 + // + this.button5.Location = new System.Drawing.Point(59, 77); + this.button5.Name = "button5"; + this.button5.Size = new System.Drawing.Size(42, 20); + this.button5.TabIndex = 57; + this.button5.Text = "-"; + this.button5.UseVisualStyleBackColor = true; + // + // button16 + // + this.button16.Location = new System.Drawing.Point(11, 77); + this.button16.Name = "button16"; + this.button16.Size = new System.Drawing.Size(42, 20); + this.button16.TabIndex = 56; + this.button16.Text = "+"; + this.button16.UseVisualStyleBackColor = true; + // + // button3 + // + this.button3.Location = new System.Drawing.Point(59, 48); + this.button3.Name = "button3"; + this.button3.Size = new System.Drawing.Size(42, 20); + this.button3.TabIndex = 55; + this.button3.Text = "-"; + this.button3.UseVisualStyleBackColor = true; + // + // button4 + // + this.button4.Location = new System.Drawing.Point(11, 48); + this.button4.Name = "button4"; + this.button4.Size = new System.Drawing.Size(42, 20); + this.button4.TabIndex = 54; + this.button4.Text = "+"; + this.button4.UseVisualStyleBackColor = true; + // + // button2 + // + this.button2.Location = new System.Drawing.Point(59, 19); + this.button2.Name = "button2"; + this.button2.Size = new System.Drawing.Size(42, 20); + this.button2.TabIndex = 53; + this.button2.Text = "-"; + this.button2.UseVisualStyleBackColor = true; + // + // button1 + // + this.button1.Location = new System.Drawing.Point(11, 19); + this.button1.Name = "button1"; + this.button1.Size = new System.Drawing.Size(42, 20); + this.button1.TabIndex = 52; + this.button1.Text = "+"; + this.button1.UseVisualStyleBackColor = true; + // + // coloredLightingInfo + // + coloredLightingInfo.Controls.Add(this.floorcolor); + coloredLightingInfo.Controls.Add(this.lowercolor); + coloredLightingInfo.Controls.Add(this.thingcolor); + coloredLightingInfo.Controls.Add(this.topcolor); + coloredLightingInfo.Controls.Add(this.ceilingcolor); + coloredLightingInfo.Location = new System.Drawing.Point(7, 162); + coloredLightingInfo.Name = "coloredLightingInfo"; + coloredLightingInfo.Size = new System.Drawing.Size(294, 167); + coloredLightingInfo.TabIndex = 5; + coloredLightingInfo.TabStop = false; + coloredLightingInfo.Text = "Colored Lighting Info"; + // + // floorcolor + // + this.floorcolor.BackColor = System.Drawing.Color.Transparent; + this.floorcolor.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.floorcolor.Label = "Floor:"; + this.floorcolor.Location = new System.Drawing.Point(16, 135); + this.floorcolor.MaximumSize = new System.Drawing.Size(10000, 23); + this.floorcolor.MinimumSize = new System.Drawing.Size(100, 23); + this.floorcolor.Name = "floorcolor"; + this.floorcolor.Size = new System.Drawing.Size(272, 23); + this.floorcolor.TabIndex = 6; + // + // lowercolor + // + this.lowercolor.BackColor = System.Drawing.Color.Transparent; + this.lowercolor.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.lowercolor.Label = "Bottom Half Wall:"; + this.lowercolor.Location = new System.Drawing.Point(16, 106); + this.lowercolor.MaximumSize = new System.Drawing.Size(10000, 23); + this.lowercolor.MinimumSize = new System.Drawing.Size(100, 23); + this.lowercolor.Name = "lowercolor"; + this.lowercolor.Size = new System.Drawing.Size(272, 23); + this.lowercolor.TabIndex = 5; + // + // thingcolor + // + this.thingcolor.BackColor = System.Drawing.Color.Transparent; + this.thingcolor.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.thingcolor.Label = "Thing:"; + this.thingcolor.Location = new System.Drawing.Point(16, 77); + this.thingcolor.MaximumSize = new System.Drawing.Size(10000, 23); + this.thingcolor.MinimumSize = new System.Drawing.Size(100, 23); + this.thingcolor.Name = "thingcolor"; + this.thingcolor.Size = new System.Drawing.Size(272, 23); + this.thingcolor.TabIndex = 4; + // + // topcolor + // + this.topcolor.BackColor = System.Drawing.Color.Transparent; + this.topcolor.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.topcolor.Label = "Top Half Wall:"; + this.topcolor.Location = new System.Drawing.Point(16, 48); + this.topcolor.MaximumSize = new System.Drawing.Size(10000, 23); + this.topcolor.MinimumSize = new System.Drawing.Size(100, 23); + this.topcolor.Name = "topcolor"; + this.topcolor.Size = new System.Drawing.Size(272, 23); + this.topcolor.TabIndex = 3; + // + // ceilingcolor + // + this.ceilingcolor.BackColor = System.Drawing.Color.Transparent; + this.ceilingcolor.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.ceilingcolor.Label = "Ceiling:"; + this.ceilingcolor.Location = new System.Drawing.Point(16, 19); + this.ceilingcolor.MaximumSize = new System.Drawing.Size(10000, 23); + this.ceilingcolor.MinimumSize = new System.Drawing.Size(100, 23); + this.ceilingcolor.Name = "ceilingcolor"; + this.ceilingcolor.Size = new System.Drawing.Size(272, 23); + this.ceilingcolor.TabIndex = 2; + // + // groupaction + // + this.groupaction.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.groupaction.Controls.Add(this.tag); + this.groupaction.Controls.Add(labelTag); + this.groupaction.Controls.Add(this.newtag); + this.groupaction.Location = new System.Drawing.Point(7, 394); + this.groupaction.Name = "groupaction"; + this.groupaction.Size = new System.Drawing.Size(436, 49); + this.groupaction.TabIndex = 2; + this.groupaction.TabStop = false; + this.groupaction.Text = " Identification "; + // + // tag + // + this.tag.AllowDecimal = false; + this.tag.AllowNegative = false; + this.tag.AllowRelative = true; + this.tag.ButtonStep = 1; + this.tag.Location = new System.Drawing.Point(89, 16); + this.tag.Name = "tag"; + this.tag.Size = new System.Drawing.Size(73, 24); + this.tag.StepValues = null; + this.tag.TabIndex = 25; + // + // newtag + // + this.newtag.Location = new System.Drawing.Point(174, 17); + this.newtag.Name = "newtag"; + this.newtag.Size = new System.Drawing.Size(76, 23); + this.newtag.TabIndex = 1; + this.newtag.Text = "New Tag"; + this.newtag.UseVisualStyleBackColor = true; + this.newtag.Click += new System.EventHandler(this.newtag_Click); + // + // groupeffect + // + this.groupeffect.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.groupeffect.Controls.Add(this.browseeffect); + this.groupeffect.Controls.Add(this.effect); + this.groupeffect.Controls.Add(labelSpecial); + this.groupeffect.Location = new System.Drawing.Point(7, 340); + this.groupeffect.Name = "groupeffect"; + this.groupeffect.Size = new System.Drawing.Size(436, 48); + this.groupeffect.TabIndex = 1; + this.groupeffect.TabStop = false; + this.groupeffect.Text = " Effects "; + // + // browseeffect + // + this.browseeffect.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.browseeffect.Image = global::CodeImp.DoomBuilder.Properties.Resources.treeview; + this.browseeffect.Location = new System.Drawing.Point(385, 21); + this.browseeffect.Name = "browseeffect"; + this.browseeffect.Padding = new System.Windows.Forms.Padding(0, 0, 1, 3); + this.browseeffect.Size = new System.Drawing.Size(30, 23); + this.browseeffect.TabIndex = 1; + this.browseeffect.Text = " "; + this.browseeffect.UseVisualStyleBackColor = true; + this.browseeffect.Click += new System.EventHandler(this.browseeffect_Click); + // + // effect + // + this.effect.BackColor = System.Drawing.Color.Transparent; + this.effect.Cursor = System.Windows.Forms.Cursors.Default; + this.effect.Empty = false; + this.effect.GeneralizedCategories = null; + this.effect.Location = new System.Drawing.Point(89, 22); + this.effect.Macro = false; + this.effect.Name = "effect"; + this.effect.Size = new System.Drawing.Size(290, 21); + this.effect.TabIndex = 0; + this.effect.Value = 402; + // + // cancel + // + this.cancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.cancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.cancel.Location = new System.Drawing.Point(353, 652); + this.cancel.Name = "cancel"; + this.cancel.Size = new System.Drawing.Size(112, 25); + this.cancel.TabIndex = 2; + this.cancel.Text = "Cancel"; + this.cancel.UseVisualStyleBackColor = true; + this.cancel.Click += new System.EventHandler(this.cancel_Click); + // + // apply + // + this.apply.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.apply.Location = new System.Drawing.Point(236, 652); + this.apply.Name = "apply"; + this.apply.Size = new System.Drawing.Size(112, 25); + this.apply.TabIndex = 1; + this.apply.Text = "OK"; + this.apply.UseVisualStyleBackColor = true; + this.apply.Click += new System.EventHandler(this.apply_Click); + // + // sectorproperties + // + this.sectorproperties.Controls.Add(coloredLightingInfo); + this.sectorproperties.Controls.Add(this.groupeffect); + this.sectorproperties.Controls.Add(this.lightsPanel); + this.sectorproperties.Controls.Add(this.settingsgroup); + this.sectorproperties.Controls.Add(this.groupaction); + this.sectorproperties.Controls.Add(groupfloorceiling); + this.sectorproperties.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.sectorproperties.Location = new System.Drawing.Point(12, 12); + this.sectorproperties.Name = "sectorproperties"; + this.sectorproperties.Padding = new System.Windows.Forms.Padding(3); + this.sectorproperties.Size = new System.Drawing.Size(449, 592); + this.sectorproperties.TabIndex = 0; + this.sectorproperties.Text = "Properties"; + // + // lightsPanel + // + this.lightsPanel.Controls.Add(incdecIntensity); + this.lightsPanel.Location = new System.Drawing.Point(7, 155); + this.lightsPanel.Name = "lightsPanel"; + this.lightsPanel.Size = new System.Drawing.Size(436, 179); + this.lightsPanel.TabIndex = 4; + // + // settingsgroup + // + this.settingsgroup.Controls.Add(this.flags); + this.settingsgroup.Location = new System.Drawing.Point(7, 449); + this.settingsgroup.Name = "settingsgroup"; + this.settingsgroup.Size = new System.Drawing.Size(436, 136); + this.settingsgroup.TabIndex = 3; + this.settingsgroup.TabStop = false; + this.settingsgroup.Text = "Settings"; + // + // flags + // + this.flags.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.flags.AutoScroll = true; + this.flags.Columns = 3; + this.flags.Location = new System.Drawing.Point(6, 14); + this.flags.Name = "flags"; + this.flags.Size = new System.Drawing.Size(424, 116); + this.flags.TabIndex = 4; + // + // flatSelectorControl2 + // + this.flatSelectorControl2.Location = new System.Drawing.Point(271, 37); + this.flatSelectorControl2.Name = "flatSelectorControl2"; + this.flatSelectorControl2.Size = new System.Drawing.Size(83, 105); + this.flatSelectorControl2.TabIndex = 13; + this.flatSelectorControl2.TextureName = ""; + // + // flatSelectorControl1 + // + this.flatSelectorControl1.Location = new System.Drawing.Point(363, 37); + this.flatSelectorControl1.Name = "flatSelectorControl1"; + this.flatSelectorControl1.Size = new System.Drawing.Size(83, 105); + this.flatSelectorControl1.TabIndex = 12; + this.flatSelectorControl1.TextureName = ""; + // + // heightpanel3 + // + this.heightpanel3.BackColor = System.Drawing.Color.Navy; + this.heightpanel3.Location = new System.Drawing.Point(128, -19); + this.heightpanel3.Name = "heightpanel3"; + this.heightpanel3.Size = new System.Drawing.Size(78, 677); + this.heightpanel3.TabIndex = 5; + this.heightpanel3.Visible = false; + // + // SectorEditForm + // + this.AcceptButton = this.apply; + this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; + this.CancelButton = this.cancel; + this.ClientSize = new System.Drawing.Size(477, 685); + this.Controls.Add(this.sectorproperties); + this.Controls.Add(this.cancel); + this.Controls.Add(this.apply); + this.Controls.Add(this.heightpanel3); + this.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "SectorEditForm"; + this.Opacity = 0D; + this.ShowIcon = false; + this.ShowInTaskbar = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Edit Sector"; + this.HelpRequested += new System.Windows.Forms.HelpEventHandler(this.SectorEditForm_HelpRequested); + groupfloorceiling.ResumeLayout(false); + groupfloorceiling.PerformLayout(); + incdecIntensity.ResumeLayout(false); + coloredLightingInfo.ResumeLayout(false); + this.groupaction.ResumeLayout(false); + this.groupaction.PerformLayout(); + this.groupeffect.ResumeLayout(false); + this.groupeffect.PerformLayout(); + this.sectorproperties.ResumeLayout(false); + this.lightsPanel.ResumeLayout(false); + this.settingsgroup.ResumeLayout(false); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.Button cancel; + private System.Windows.Forms.Button apply; + private System.Windows.Forms.Panel sectorproperties; + private CodeImp.DoomBuilder.Controls.FlatSelectorControl floortex; + private CodeImp.DoomBuilder.Controls.FlatSelectorControl ceilingtex; + private CodeImp.DoomBuilder.Controls.FlatSelectorControl flatSelectorControl2; + private CodeImp.DoomBuilder.Controls.FlatSelectorControl flatSelectorControl1; + private System.Windows.Forms.Label sectorheight; + private CodeImp.DoomBuilder.Controls.ActionSelectorControl effect; + private System.Windows.Forms.Button newtag; + private System.Windows.Forms.Button browseeffect; + private System.Windows.Forms.Label sectorheightlabel; + private CodeImp.DoomBuilder.Controls.ButtonsNumericTextbox ceilingheight; + private CodeImp.DoomBuilder.Controls.ButtonsNumericTextbox floorheight; + private CodeImp.DoomBuilder.Controls.ButtonsNumericTextbox tag; + private System.Windows.Forms.GroupBox settingsgroup; + private CodeImp.DoomBuilder.Controls.CheckboxArrayControl flags; + private System.Windows.Forms.GroupBox groupeffect; + private System.Windows.Forms.GroupBox groupaction; + private System.Windows.Forms.Panel lightsPanel; + private System.Windows.Forms.Button button19; + private System.Windows.Forms.Button button20; + private System.Windows.Forms.Button button17; + private System.Windows.Forms.Button button18; + private System.Windows.Forms.Button button5; + private System.Windows.Forms.Button button16; + private System.Windows.Forms.Button button3; + private System.Windows.Forms.Button button4; + private System.Windows.Forms.Button button2; + private System.Windows.Forms.Button button1; + private Controls.ColorControlSector floorcolor; + private Controls.ColorControlSector lowercolor; + private Controls.ColorControlSector thingcolor; + private Controls.ColorControlSector topcolor; + private Controls.ColorControlSector ceilingcolor; + private System.Windows.Forms.Panel heightpanel3; // villsa + } +} diff --git a/Source/Core/Windows/SectorEditForm.cs b/Source/Core/Windows/SectorEditForm.cs new file mode 100644 index 0000000..8733ffb --- /dev/null +++ b/Source/Core/Windows/SectorEditForm.cs @@ -0,0 +1,405 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Text; +using System.Windows.Forms; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Data; +using CodeImp.DoomBuilder.IO; +using System.IO; +using CodeImp.DoomBuilder.Config; +using CodeImp.DoomBuilder.Editing; +using CodeImp.DoomBuilder.Controls; +using CodeImp.DoomBuilder.Rendering; // villsa + +#endregion + +namespace CodeImp.DoomBuilder.Windows +{ + internal partial class SectorEditForm : DelayedForm + { + // Variables + private ICollection sectors; + private PixelColor color; + private PixelColor[] initialcolor; + private const float LIGHTINCVALUE = 0.235f; + private const float LIGHTDECVALUE = -0.1825f; + + // Constructor + public SectorEditForm() + { + // Initialize + InitializeComponent(); + + color = new PixelColor(255, 255, 255, 255); + initialcolor = new PixelColor[Sector.NUM_COLORS]; + + // Fill effects list + effect.AddInfo(General.Map.Config.SortedSectorEffects.ToArray()); + + // villsa + if (General.Map.FormatInterface.InDoom64Mode) + { + // Fill flags list + foreach (KeyValuePair lf in General.Map.Config.SectorFlags) + flags.Add(lf.Value, lf.Key); + + groupeffect.Height = 64; + groupaction.Top = groupeffect.Bottom + groupeffect.Margin.Bottom + groupaction.Margin.Top; + settingsgroup.Top = groupaction.Bottom + groupaction.Margin.Bottom + settingsgroup.Margin.Top; + this.Height = settingsgroup.Bottom + settingsgroup.Margin.Bottom + 120; + } + + // Initialize image selectors + floortex.Initialize(); + ceilingtex.Initialize(); + this.Height = heightpanel3.Height; + } + + // This sets up the form to edit the given sectors + public void Setup(ICollection sectors) + { + Sector sc; + + // Keep this list + this.sectors = sectors; + if (sectors.Count > 1) this.Text = "Edit Sectors (" + sectors.Count + ")"; + + //////////////////////////////////////////////////////////////////////// + // Set all options to the first sector properties + //////////////////////////////////////////////////////////////////////// + + // Get first sector + sc = General.GetByIndex(sectors, 0); + + if (General.Map.FormatInterface.InDoom64Mode) + { + // villsa - Flags + foreach (CheckBox c in flags.Checkboxes) + if (sc.Flags.ContainsKey(c.Tag.ToString())) c.Checked = sc.Flags[c.Tag.ToString()]; + + ceilingcolor.Color = initialcolor[0] = sc.CeilColor.color; + topcolor.Color = initialcolor[1] = sc.TopColor.color; + thingcolor.Color = initialcolor[2] = sc.ThingColor.color; + lowercolor.Color = initialcolor[3] = sc.LowerColor.color; + floorcolor.Color = initialcolor[4] = sc.FloorColor.color; + } + + // Effects + effect.Value = sc.Effect; + + // Floor/ceiling + floorheight.Text = sc.FloorHeight.ToString(); + ceilingheight.Text = sc.CeilHeight.ToString(); + floortex.TextureName = sc.FloorTexture; + ceilingtex.TextureName = sc.CeilTexture; + + // Action + tag.Text = sc.Tag.ToString(); + + //////////////////////////////////////////////////////////////////////// + // Now go for all sectors and change the options when a setting is different + //////////////////////////////////////////////////////////////////////// + + // Go for all sectors + foreach (Sector s in sectors) + { + // Flags + foreach (CheckBox c in flags.Checkboxes) + { + if (s.Flags.ContainsKey(c.Tag.ToString())) + { + if (s.Flags[c.Tag.ToString()] != c.Checked) + { + c.ThreeState = true; + c.CheckState = CheckState.Indeterminate; + } + } + } + + // Effects + if (s.Effect != effect.Value) effect.Empty = true; + + // Floor/Ceiling + if (s.FloorHeight.ToString() != floorheight.Text) floorheight.Text = ""; + if (s.CeilHeight.ToString() != ceilingheight.Text) ceilingheight.Text = ""; + if (s.FloorTexture != floortex.TextureName) floortex.TextureName = ""; + if (s.CeilTexture != ceilingtex.TextureName) ceilingtex.TextureName = ""; + + // Action + if (s.Tag.ToString() != tag.Text) tag.Text = ""; + } + + // Show sector height + UpdateSectorHeight(); + } + + // This updates the sector height field + private void UpdateSectorHeight() + { + bool showheight = true; + int delta = 0; + Sector first = null; + + // Check all selected sectors + foreach (Sector s in sectors) + { + if (first == null) + { + // First sector in list + delta = s.CeilHeight - s.FloorHeight; + showheight = true; + first = s; + } + else + { + if (delta != (s.CeilHeight - s.FloorHeight)) + { + // We can't show heights because the delta + // heights for the sectors is different + showheight = false; + break; + } + } + } + + if (showheight) + { + int fh = floorheight.GetResult(first.FloorHeight); + int ch = ceilingheight.GetResult(first.CeilHeight); + int height = ch - fh; + sectorheight.Text = height.ToString(); + sectorheight.Visible = true; + sectorheightlabel.Visible = true; + } + else + { + sectorheight.Visible = false; + sectorheightlabel.Visible = false; + } + } + + // OK clicked + private void apply_Click(object sender, EventArgs e) + { + string undodesc = "sector"; + + // Verify the tag + if ((tag.GetResult(0) < General.Map.FormatInterface.MinTag) || (tag.GetResult(0) > General.Map.FormatInterface.MaxTag)) + { + General.ShowWarningMessage("Sector tag must be between " + General.Map.FormatInterface.MinTag + " and " + General.Map.FormatInterface.MaxTag + ".", MessageBoxButtons.OK); + return; + } + + // Verify the effect + if ((effect.Value < General.Map.FormatInterface.MinEffect) || (effect.Value > General.Map.FormatInterface.MaxEffect)) + { + General.ShowWarningMessage("Sector effect must be between " + General.Map.FormatInterface.MinEffect + " and " + General.Map.FormatInterface.MaxEffect + ".", MessageBoxButtons.OK); + return; + } + + // Make undo + if (sectors.Count > 1) undodesc = sectors.Count + " sectors"; + General.Map.UndoRedo.CreateUndo("Edit " + undodesc); + + // Go for all sectors + foreach (Sector s in sectors) + { + // villsa - Apply all flags + if (General.Map.FormatInterface.InDoom64Mode) + { + Lights light = new Lights(); + + // flags + foreach (CheckBox c in flags.Checkboxes) + { + if (c.CheckState == CheckState.Checked) s.SetFlag(c.Tag.ToString(), true); + else if (c.CheckState == CheckState.Unchecked) s.SetFlag(c.Tag.ToString(), false); + } + + // + // color lights + // + + if (initialcolor[0].ToColor() != ceilingcolor.Color.ToColor()) + { + light.color = ceilingcolor.Color; + s.CeilColor = light; + } + + if (initialcolor[1].ToColor() != topcolor.Color.ToColor()) + { + light.color = topcolor.Color; + s.TopColor = light; + } + + if (initialcolor[2].ToColor() != thingcolor.Color.ToColor()) + { + light.color = thingcolor.Color; + s.ThingColor = light; + } + + if (initialcolor[3].ToColor() != lowercolor.Color.ToColor()) + { + light.color = lowercolor.Color; + s.LowerColor = light; + } + + if (initialcolor[4].ToColor() != floorcolor.Color.ToColor()) + { + light.color = floorcolor.Color; + s.FloorColor = light; + } + } + + // Effects + if (!effect.Empty) s.Effect = effect.Value; + + // Floor/Ceiling + s.FloorHeight = floorheight.GetResult(s.FloorHeight); + s.CeilHeight = ceilingheight.GetResult(s.CeilHeight); + s.SetFloorTexture(floortex.GetResult(s.FloorTexture)); + s.SetCeilTexture(ceilingtex.GetResult(s.CeilTexture)); + + // Action + s.Tag = General.Clamp(tag.GetResult(s.Tag), General.Map.FormatInterface.MinTag, General.Map.FormatInterface.MaxTag); + } + + // Update the used textures + General.Map.Data.UpdateUsedTextures(); + + // Done + General.Map.IsChanged = true; + this.DialogResult = DialogResult.OK; + this.Close(); + } + + // Cancel clicked + private void cancel_Click(object sender, EventArgs e) + { + // Be gone + this.DialogResult = DialogResult.Cancel; + this.Close(); + } + + // This finds a new (unused) tag + private void newtag_Click(object sender, EventArgs e) + { + tag.Text = General.Map.Map.GetNewTag().ToString(); + } + + // Browse Effect clicked + private void browseeffect_Click(object sender, EventArgs e) + { + effect.Value = EffectBrowserForm.BrowseEffect(this, effect.Value); + } + + // Ceiling height changes + private void ceilingheight_TextChanged(object sender, EventArgs e) + { + UpdateSectorHeight(); + } + + // Floor height changes + private void floorheight_TextChanged(object sender, EventArgs e) + { + UpdateSectorHeight(); + } + + // Help + private void SectorEditForm_HelpRequested(object sender, HelpEventArgs hlpevent) + { + General.ShowHelp("w_sectoredit.html"); + hlpevent.Handled = true; + } + + private void button1_Click(object sender, EventArgs e) + { + Lights light = new Lights(ceilingcolor.Color.r, ceilingcolor.Color.g, ceilingcolor.Color.b, 0); + light.SetIntensity(LIGHTINCVALUE); + ceilingcolor.Color = light.color; + } + + private void button4_Click(object sender, EventArgs e) + { + Lights light = new Lights(topcolor.Color.r, topcolor.Color.g, topcolor.Color.b, 0); + light.SetIntensity(LIGHTINCVALUE); + topcolor.Color = light.color; + } + + private void button16_Click(object sender, EventArgs e) + { + Lights light = new Lights(thingcolor.Color.r, thingcolor.Color.g, thingcolor.Color.b, 0); + light.SetIntensity(LIGHTINCVALUE); + thingcolor.Color = light.color; + } + + private void button18_Click(object sender, EventArgs e) + { + Lights light = new Lights(lowercolor.Color.r, lowercolor.Color.g, lowercolor.Color.b, 0); + light.SetIntensity(LIGHTINCVALUE); + lowercolor.Color = light.color; + } + + private void button20_Click(object sender, EventArgs e) + { + Lights light = new Lights(floorcolor.Color.r, floorcolor.Color.g, floorcolor.Color.b, 0); + light.SetIntensity(LIGHTINCVALUE); + floorcolor.Color = light.color; + } + + private void button2_Click(object sender, EventArgs e) + { + Lights light = new Lights(ceilingcolor.Color.r, ceilingcolor.Color.g, ceilingcolor.Color.b, 0); + light.SetIntensity(LIGHTDECVALUE); + ceilingcolor.Color = light.color; + } + + private void button3_Click(object sender, EventArgs e) + { + Lights light = new Lights(topcolor.Color.r, topcolor.Color.g, topcolor.Color.b, 0); + light.SetIntensity(LIGHTDECVALUE); + topcolor.Color = light.color; + } + + private void button5_Click(object sender, EventArgs e) + { + Lights light = new Lights(thingcolor.Color.r, thingcolor.Color.g, thingcolor.Color.b, 0); + light.SetIntensity(LIGHTDECVALUE); + thingcolor.Color = light.color; + } + + private void button17_Click(object sender, EventArgs e) + { + Lights light = new Lights(lowercolor.Color.r, lowercolor.Color.g, lowercolor.Color.b, 0); + light.SetIntensity(LIGHTDECVALUE); + lowercolor.Color = light.color; + } + + private void button19_Click(object sender, EventArgs e) + { + Lights light = new Lights(floorcolor.Color.r, floorcolor.Color.g, floorcolor.Color.b, 0); + light.SetIntensity(LIGHTDECVALUE); + floorcolor.Color = light.color; + } + } +} diff --git a/Source/Core/Windows/SectorEditForm.resx b/Source/Core/Windows/SectorEditForm.resx new file mode 100644 index 0000000..69e2302 --- /dev/null +++ b/Source/Core/Windows/SectorEditForm.resx @@ -0,0 +1,174 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + False + + + False + + + False + + + False + + + False + + + True + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + True + + + False + + + True + + \ No newline at end of file diff --git a/Source/Core/Windows/StatusInfo.cs b/Source/Core/Windows/StatusInfo.cs new file mode 100644 index 0000000..e044ec6 --- /dev/null +++ b/Source/Core/Windows/StatusInfo.cs @@ -0,0 +1,82 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using System.Drawing; +using System.ComponentModel; +using CodeImp.DoomBuilder.Map; +using SlimDX.Direct3D9; +using SlimDX; +using CodeImp.DoomBuilder.Geometry; +using System.Drawing.Imaging; +using CodeImp.DoomBuilder.Data; +using CodeImp.DoomBuilder.Editing; + +#endregion + +namespace CodeImp.DoomBuilder.Windows +{ + public struct StatusInfo + { + public StatusType type; + public string message; + internal bool displayed; + + internal StatusInfo(StatusType type, string message) + { + this.type = type; + this.message = message; + this.displayed = false; + } + } + + public enum StatusType : int + { + /// + /// When no particular information is to be displayed. The messages displayed depends on running background processes. + /// + Ready, + + /// + /// Shows action information and flashes up the status icon once. + /// + Action, + + /// + /// Shows information without flashing the icon. + /// + Info, + + /// + /// Shows information with the busy icon. + /// + Busy, + + /// + /// Shows a warning, makes a warning sound and flashes a warning icon. + /// + Warning + } +} diff --git a/Source/Core/Windows/TanColorTable.cs b/Source/Core/Windows/TanColorTable.cs new file mode 100644 index 0000000..b454462 --- /dev/null +++ b/Source/Core/Windows/TanColorTable.cs @@ -0,0 +1,786 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Runtime.InteropServices; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using System.Drawing; +using System.ComponentModel; +using System.Windows.Forms.VisualStyles; +using CodeImp.DoomBuilder.Map; +using Microsoft.Win32; +using SlimDX.Direct3D9; +using SlimDX; +using CodeImp.DoomBuilder.Geometry; +using System.Drawing.Imaging; +using CodeImp.DoomBuilder.Data; +using CodeImp.DoomBuilder.Editing; + +#endregion + +namespace CodeImp.DoomBuilder.Windows +{ + // Taken from http://blogs.msdn.com/jfoscoding/archive/2005/11/06/489641.aspx + + internal class TanColorTable : ProfessionalColorTable + { + // Methods + public TanColorTable() + { + } + + internal Color FromKnownColor(TanColorTable.KnownColors color) + { + return (Color)this.ColorTable[color]; + } + + internal static void InitTanLunaColors(ref Dictionary rgbTable) + { + rgbTable[TanColorTable.KnownColors.GripDark] = Color.FromArgb(0xc1, 190, 0xb3); + rgbTable[TanColorTable.KnownColors.SeparatorDark] = Color.FromArgb(0xc5, 0xc2, 0xb8); + rgbTable[TanColorTable.KnownColors.MenuItemSelected] = Color.FromArgb(0xc1, 210, 0xee); + rgbTable[TanColorTable.KnownColors.ButtonPressedBorder] = Color.FromArgb(0x31, 0x6a, 0xc5); + rgbTable[TanColorTable.KnownColors.CheckBackground] = Color.FromArgb(0xe1, 230, 0xe8); + rgbTable[TanColorTable.KnownColors.MenuItemBorder] = Color.FromArgb(0x31, 0x6a, 0xc5); + rgbTable[TanColorTable.KnownColors.CheckBackgroundMouseOver] = Color.FromArgb(0x31, 0x6a, 0xc5); + rgbTable[TanColorTable.KnownColors.MenuItemBorderMouseOver] = Color.FromArgb(0x4b, 0x4b, 0x6f); + rgbTable[TanColorTable.KnownColors.ToolStripDropDownBackground] = Color.FromArgb(0xfc, 0xfc, 0xf9); + rgbTable[TanColorTable.KnownColors.MenuBorder] = Color.FromArgb(0x8a, 0x86, 0x7a); + rgbTable[TanColorTable.KnownColors.SeparatorLight] = Color.FromArgb(0xff, 0xff, 0xff); + rgbTable[TanColorTable.KnownColors.ToolStripBorder] = Color.FromArgb(0xa3, 0xa3, 0x7c); + rgbTable[TanColorTable.KnownColors.MenuStripGradientBegin] = Color.FromArgb(0xe5, 0xe5, 0xd7); + rgbTable[TanColorTable.KnownColors.MenuStripGradientEnd] = Color.FromArgb(0xf4, 0xf2, 0xe8); + rgbTable[TanColorTable.KnownColors.ImageMarginGradientBegin] = Color.FromArgb(0xfe, 0xfe, 0xfb); + rgbTable[TanColorTable.KnownColors.ImageMarginGradientMiddle] = Color.FromArgb(0xec, 0xe7, 0xe0); + rgbTable[TanColorTable.KnownColors.ImageMarginGradientEnd] = Color.FromArgb(0xbd, 0xbd, 0xa3); + rgbTable[TanColorTable.KnownColors.OverflowButtonGradientBegin] = Color.FromArgb(0xf3, 0xf2, 240); + rgbTable[TanColorTable.KnownColors.OverflowButtonGradientMiddle] = Color.FromArgb(0xe2, 0xe1, 0xdb); + rgbTable[TanColorTable.KnownColors.OverflowButtonGradientEnd] = Color.FromArgb(0x92, 0x92, 0x76); + rgbTable[TanColorTable.KnownColors.MenuItemPressedGradientBegin] = Color.FromArgb(0xfc, 0xfc, 0xf9); + rgbTable[TanColorTable.KnownColors.MenuItemPressedGradientEnd] = Color.FromArgb(0xf6, 0xf4, 0xec); + rgbTable[TanColorTable.KnownColors.ImageMarginRevealedGradientBegin] = Color.FromArgb(0xf7, 0xf6, 0xef); + rgbTable[TanColorTable.KnownColors.ImageMarginRevealedGradientMiddle] = Color.FromArgb(0xf2, 240, 0xe4); + rgbTable[TanColorTable.KnownColors.ImageMarginRevealedGradientEnd] = Color.FromArgb(230, 0xe3, 210); + rgbTable[TanColorTable.KnownColors.ButtonCheckedGradientBegin] = Color.FromArgb(0xe1, 230, 0xe8); + rgbTable[TanColorTable.KnownColors.ButtonCheckedGradientMiddle] = Color.FromArgb(0xe1, 230, 0xe8); + rgbTable[TanColorTable.KnownColors.ButtonCheckedGradientEnd] = Color.FromArgb(0xe1, 230, 0xe8); + rgbTable[TanColorTable.KnownColors.ButtonSelectedGradientBegin] = Color.FromArgb(0xc1, 210, 0xee); + rgbTable[TanColorTable.KnownColors.ButtonSelectedGradientMiddle] = Color.FromArgb(0xc1, 210, 0xee); + rgbTable[TanColorTable.KnownColors.ButtonSelectedGradientEnd] = Color.FromArgb(0xc1, 210, 0xee); + rgbTable[TanColorTable.KnownColors.ButtonPressedGradientBegin] = Color.FromArgb(0x98, 0xb5, 0xe2); + rgbTable[TanColorTable.KnownColors.ButtonPressedGradientMiddle] = Color.FromArgb(0x98, 0xb5, 0xe2); + rgbTable[TanColorTable.KnownColors.ButtonPressedGradientEnd] = Color.FromArgb(0x98, 0xb5, 0xe2); + rgbTable[TanColorTable.KnownColors.GripLight] = Color.FromArgb(0xff, 0xff, 0xff); + } + + public override Color ButtonCheckedGradientBegin + { + get + { + if (!this.UseBaseColorTable) + { + return this.FromKnownColor(TanColorTable.KnownColors.ButtonCheckedGradientBegin); + } + return base.ButtonCheckedGradientBegin; + } + } + + public override Color ButtonCheckedGradientEnd + { + get + { + if (!this.UseBaseColorTable) + { + return this.FromKnownColor(TanColorTable.KnownColors.ButtonCheckedGradientEnd); + } + return base.ButtonCheckedGradientEnd; + } + } + + public override Color ButtonCheckedGradientMiddle + { + get + { + if (!this.UseBaseColorTable) + { + return this.FromKnownColor(TanColorTable.KnownColors.ButtonCheckedGradientMiddle); + } + return base.ButtonCheckedGradientMiddle; + } + } + + + public override Color ButtonPressedBorder + { + get + { + if (!this.UseBaseColorTable) + { + return this.FromKnownColor(TanColorTable.KnownColors.ButtonPressedBorder); + } + return base.ButtonPressedBorder; + } + } + + public override Color ButtonPressedGradientBegin + { + get + { + if (!this.UseBaseColorTable) + { + return this.FromKnownColor(TanColorTable.KnownColors.ButtonPressedGradientBegin); + } + return base.ButtonPressedGradientBegin; + } + } + + public override Color ButtonPressedGradientEnd + { + get + { + if (!this.UseBaseColorTable) + { + return this.FromKnownColor(TanColorTable.KnownColors.ButtonPressedGradientEnd); + } + return base.ButtonPressedGradientEnd; + } + } + + + public override Color ButtonPressedGradientMiddle + { + get + { + if (!this.UseBaseColorTable) + { + return this.FromKnownColor(TanColorTable.KnownColors.ButtonPressedGradientMiddle); + } + return base.ButtonPressedGradientMiddle; + } + } + + public override Color ButtonSelectedBorder + { + get + { + if (!this.UseBaseColorTable) + { + return this.FromKnownColor(TanColorTable.KnownColors.ButtonPressedBorder); + } + return base.ButtonSelectedBorder; + } + } + + + public override Color ButtonSelectedGradientBegin + { + get + { + if (!this.UseBaseColorTable) + { + return this.FromKnownColor(TanColorTable.KnownColors.ButtonSelectedGradientBegin); + } + return base.ButtonSelectedGradientBegin; + } + } + public override Color ButtonSelectedGradientEnd + { + get + { + if (!this.UseBaseColorTable) + { + return this.FromKnownColor(TanColorTable.KnownColors.ButtonSelectedGradientEnd); + } + return base.ButtonSelectedGradientEnd; + } + } + public override Color ButtonSelectedGradientMiddle + { + get + { + if (!this.UseBaseColorTable) + { + return this.FromKnownColor(TanColorTable.KnownColors.ButtonSelectedGradientMiddle); + } + return base.ButtonSelectedGradientMiddle; + } + } + + public override Color CheckBackground + { + get + { + if (!this.UseBaseColorTable) + { + return this.FromKnownColor(TanColorTable.KnownColors.CheckBackground); + } + return base.CheckBackground; + } + } + + public override Color CheckPressedBackground + { + get + { + if (!this.UseBaseColorTable) + { + return this.FromKnownColor(TanColorTable.KnownColors.CheckBackgroundMouseOver); + } + return base.CheckPressedBackground; + } + } + + public override Color CheckSelectedBackground + { + get + { + if (!this.UseBaseColorTable) + { + return this.FromKnownColor(TanColorTable.KnownColors.CheckBackgroundMouseOver); + } + return base.CheckSelectedBackground; + } + } + + + internal static string ColorScheme + { + get + { + return TanColorTable.DisplayInformation.ColorScheme; + } + } + + private Dictionary ColorTable + { + get + { + if (this.tanRGB == null) + { + this.tanRGB = new Dictionary((int)KnownColors.LastKnownColor); + TanColorTable.InitTanLunaColors(ref this.tanRGB); + } + return this.tanRGB; + } + } + + public override Color GripDark + { + get + { + if (!this.UseBaseColorTable) + { + return this.FromKnownColor(TanColorTable.KnownColors.GripDark); + } + return base.GripDark; + } + } + + public override Color GripLight + { + get + { + if (!this.UseBaseColorTable) + { + return this.FromKnownColor(TanColorTable.KnownColors.GripLight); + } + return base.GripLight; + } + } + public override Color ImageMarginGradientBegin + { + get + { + if (!this.UseBaseColorTable) + { + return this.FromKnownColor(TanColorTable.KnownColors.ImageMarginGradientBegin); + } + return base.ImageMarginGradientBegin; + } + } + + public override Color ImageMarginGradientEnd + { + get + { + if (!this.UseBaseColorTable) + { + return this.FromKnownColor(TanColorTable.KnownColors.ImageMarginGradientEnd); + } + return base.ImageMarginGradientEnd; + } + } + + public override Color ImageMarginGradientMiddle + { + get + { + if (!this.UseBaseColorTable) + { + return this.FromKnownColor(TanColorTable.KnownColors.ImageMarginGradientMiddle); + } + return base.ImageMarginGradientMiddle; + } + } + + public override Color ImageMarginRevealedGradientBegin + { + get + { + if (!this.UseBaseColorTable) + { + return this.FromKnownColor(TanColorTable.KnownColors.ImageMarginRevealedGradientBegin); + } + return base.ImageMarginRevealedGradientBegin; + } + } + + public override Color ImageMarginRevealedGradientEnd + { + get + { + if (!this.UseBaseColorTable) + { + return this.FromKnownColor(TanColorTable.KnownColors.ImageMarginRevealedGradientEnd); + } + return base.ImageMarginRevealedGradientEnd; + } + } + + public override Color ImageMarginRevealedGradientMiddle + { + get + { + if (!this.UseBaseColorTable) + { + return this.FromKnownColor(TanColorTable.KnownColors.ImageMarginRevealedGradientMiddle); + } + return base.ImageMarginRevealedGradientMiddle; + } + } + + public override Color MenuBorder + { + get + { + if (!this.UseBaseColorTable) + { + return this.FromKnownColor(TanColorTable.KnownColors.MenuBorder); + } + return base.MenuItemBorder; + } + } + + + public override Color MenuItemBorder + { + get + { + if (!this.UseBaseColorTable) + { + return this.FromKnownColor(TanColorTable.KnownColors.MenuItemBorder); + } + return base.MenuItemBorder; + } + } + + public override Color MenuItemPressedGradientBegin + { + get + { + if (!this.UseBaseColorTable) + { + return this.FromKnownColor(TanColorTable.KnownColors.MenuItemPressedGradientBegin); + } + return base.MenuItemPressedGradientBegin; + } + } + + public override Color MenuItemPressedGradientEnd + { + get + { + if (!this.UseBaseColorTable) + { + return this.FromKnownColor(TanColorTable.KnownColors.MenuItemPressedGradientEnd); + } + return base.MenuItemPressedGradientEnd; + } + } + + public override Color MenuItemPressedGradientMiddle + { + get + { + if (!this.UseBaseColorTable) + { + return this.FromKnownColor(TanColorTable.KnownColors.ImageMarginRevealedGradientMiddle); + } + return base.MenuItemPressedGradientMiddle; + } + } + + public override Color MenuItemSelected + { + get + { + if (!this.UseBaseColorTable) + { + return this.FromKnownColor(TanColorTable.KnownColors.MenuItemSelected); + } + return base.MenuItemSelected; + } + } + + public override Color MenuItemSelectedGradientBegin + { + get + { + if (!this.UseBaseColorTable) + { + return this.FromKnownColor(TanColorTable.KnownColors.ButtonSelectedGradientBegin); + } + return base.MenuItemSelectedGradientBegin; + } + } + + public override Color MenuItemSelectedGradientEnd + { + get + { + if (!this.UseBaseColorTable) + { + return this.FromKnownColor(TanColorTable.KnownColors.ButtonSelectedGradientEnd); + } + return base.MenuItemSelectedGradientEnd; + } + } + + + public override Color MenuStripGradientBegin + { + get + { + if (!this.UseBaseColorTable) + { + return this.FromKnownColor(TanColorTable.KnownColors.MenuStripGradientBegin); + } + return base.MenuStripGradientBegin; + } + } + + public override Color MenuStripGradientEnd + { + get + { + if (!this.UseBaseColorTable) + { + return this.FromKnownColor(TanColorTable.KnownColors.MenuStripGradientEnd); + } + return base.MenuStripGradientEnd; + } + } + + public override Color OverflowButtonGradientBegin + { + get + { + if (!this.UseBaseColorTable) + { + return this.FromKnownColor(TanColorTable.KnownColors.OverflowButtonGradientBegin); + } + return base.OverflowButtonGradientBegin; + } + } + + public override Color OverflowButtonGradientEnd + { + get + { + if (!this.UseBaseColorTable) + { + return this.FromKnownColor(TanColorTable.KnownColors.OverflowButtonGradientEnd); + } + return base.OverflowButtonGradientEnd; + } + } + + public override Color OverflowButtonGradientMiddle + { + get + { + if (!this.UseBaseColorTable) + { + return this.FromKnownColor(TanColorTable.KnownColors.OverflowButtonGradientMiddle); + } + return base.OverflowButtonGradientMiddle; + } + } + + + public override Color RaftingContainerGradientBegin + { + get + { + if (!this.UseBaseColorTable) + { + return this.FromKnownColor(TanColorTable.KnownColors.MenuStripGradientBegin); + } + return base.RaftingContainerGradientBegin; + } + } + + public override Color RaftingContainerGradientEnd + { + get + { + if (!this.UseBaseColorTable) + { + return this.FromKnownColor(TanColorTable.KnownColors.MenuStripGradientEnd); + } + return base.RaftingContainerGradientEnd; + } + } + + + public override Color SeparatorDark + { + get + { + if (!this.UseBaseColorTable) + { + return this.FromKnownColor(TanColorTable.KnownColors.SeparatorDark); + } + return base.SeparatorDark; + } + } + + public override Color SeparatorLight + { + get + { + if (!this.UseBaseColorTable) + { + return this.FromKnownColor(TanColorTable.KnownColors.SeparatorLight); + } + return base.SeparatorLight; + } + } + + public override Color ToolStripBorder + { + get + { + if (!this.UseBaseColorTable) + { + return this.FromKnownColor(TanColorTable.KnownColors.ToolStripBorder); + } + return base.ToolStripBorder; + } + } + + + public override Color ToolStripDropDownBackground + { + get + { + if (!this.UseBaseColorTable) + { + return this.FromKnownColor(TanColorTable.KnownColors.ToolStripDropDownBackground); + } + return base.ToolStripDropDownBackground; + } + } + + public override Color ToolStripGradientBegin + { + get + { + if (!this.UseBaseColorTable) + { + return this.FromKnownColor(TanColorTable.KnownColors.ImageMarginGradientBegin); + } + return base.ToolStripGradientBegin; + } + } + + public override Color ToolStripGradientEnd + { + get + { + if (!this.UseBaseColorTable) + { + return this.FromKnownColor(TanColorTable.KnownColors.ImageMarginGradientEnd); + } + return base.ToolStripGradientEnd; + } + } + + public override Color ToolStripGradientMiddle + { + get + { + if (!this.UseBaseColorTable) + { + return this.FromKnownColor(TanColorTable.KnownColors.ImageMarginGradientMiddle); + } + return base.ToolStripGradientMiddle; + } + } + + private bool UseBaseColorTable + { + get + { + bool flag1 = !TanColorTable.DisplayInformation.IsLunaTheme || + ((TanColorTable.ColorScheme != oliveColorScheme) && + (TanColorTable.ColorScheme != blueColorScheme)); + if (flag1 && (this.tanRGB != null)) + { + this.tanRGB.Clear(); + this.tanRGB = null; + } + return flag1; + } + } + + + + // Fields + private const string blueColorScheme = "NormalColor"; + private const string oliveColorScheme = "HomeStead"; + private const string silverColorScheme = "Metallic"; + private Dictionary tanRGB; + + // Nested Types + private static class DisplayInformation + { + // Methods + static DisplayInformation() + { + SystemEvents.UserPreferenceChanged += TanColorTable.DisplayInformation.OnUserPreferenceChanged; + TanColorTable.DisplayInformation.SetScheme(); + } + + private static void OnUserPreferenceChanged(object sender, UserPreferenceChangedEventArgs e) + { + TanColorTable.DisplayInformation.SetScheme(); + } + + private static void SetScheme() + { + TanColorTable.DisplayInformation.isLunaTheme = false; + if (VisualStyleRenderer.IsSupported) + { + DisplayInformation.colorScheme = VisualStyleInformation.ColorScheme; + + if (!VisualStyleInformation.IsEnabledByUser) + { + return; + } + StringBuilder builder1 = new StringBuilder(0x200); + GetCurrentThemeName(builder1, builder1.Capacity, null, 0, null, 0); + string text1 = builder1.ToString(); + TanColorTable.DisplayInformation.isLunaTheme = string.Equals(lunaFileName, Path.GetFileName(text1), StringComparison.InvariantCultureIgnoreCase); + } + else + { + TanColorTable.DisplayInformation.colorScheme = null; + } + } + + + // Properties + + public static string ColorScheme + { + get + { + return colorScheme; + } + } + + internal static bool IsLunaTheme + { + get + { + return isLunaTheme; + } + } + + // Fields + [ThreadStatic] + private static string colorScheme; + [ThreadStatic] + private static bool isLunaTheme; + private const string lunaFileName = "luna.msstyles"; + + [DllImport("uxtheme.dll", CharSet = CharSet.Auto)] + public static extern int GetCurrentThemeName(StringBuilder pszThemeFileName, int dwMaxNameChars, StringBuilder pszColorBuff, int dwMaxColorChars, StringBuilder pszSizeBuff, int cchMaxSizeChars); + + } + + internal enum KnownColors + { + ButtonPressedBorder, + MenuItemBorder, + MenuItemBorderMouseOver, + MenuItemSelected, + CheckBackground, + CheckBackgroundMouseOver, + GripDark, + GripLight, + MenuStripGradientBegin, + MenuStripGradientEnd, + ImageMarginRevealedGradientBegin, + ImageMarginRevealedGradientEnd, + ImageMarginRevealedGradientMiddle, + MenuItemPressedGradientBegin, + MenuItemPressedGradientEnd, + ButtonPressedGradientBegin, + ButtonPressedGradientEnd, + ButtonPressedGradientMiddle, + ButtonSelectedGradientBegin, + ButtonSelectedGradientEnd, + ButtonSelectedGradientMiddle, + OverflowButtonGradientBegin, + OverflowButtonGradientEnd, + OverflowButtonGradientMiddle, + ButtonCheckedGradientBegin, + ButtonCheckedGradientEnd, + ButtonCheckedGradientMiddle, + ImageMarginGradientBegin, + ImageMarginGradientEnd, + ImageMarginGradientMiddle, + MenuBorder, + ToolStripDropDownBackground, + ToolStripBorder, + SeparatorDark, + SeparatorLight, + LastKnownColor = SeparatorLight, + + } + } + +} \ No newline at end of file diff --git a/Source/Core/Windows/TextEditForm.Designer.cs b/Source/Core/Windows/TextEditForm.Designer.cs new file mode 100644 index 0000000..428eea7 --- /dev/null +++ b/Source/Core/Windows/TextEditForm.Designer.cs @@ -0,0 +1,112 @@ +namespace CodeImp.DoomBuilder.Windows +{ + /// + /// Dialog window that allows entering a multiline text string. + /// + partial class TextEditForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.cancel = new System.Windows.Forms.Button(); + this.apply = new System.Windows.Forms.Button(); + this.textbox = new System.Windows.Forms.TextBox(); + this.SuspendLayout(); + // + // cancel + // + this.cancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.cancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.cancel.Location = new System.Drawing.Point(354, 220); + this.cancel.Margin = new System.Windows.Forms.Padding(1); + this.cancel.Name = "cancel"; + this.cancel.Size = new System.Drawing.Size(112, 25); + this.cancel.TabIndex = 2; + this.cancel.TabStop = false; + this.cancel.Text = "Cancel"; + this.cancel.UseVisualStyleBackColor = true; + this.cancel.Click += new System.EventHandler(this.cancel_Click); + // + // apply + // + this.apply.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.apply.Location = new System.Drawing.Point(235, 220); + this.apply.Margin = new System.Windows.Forms.Padding(1); + this.apply.Name = "apply"; + this.apply.Size = new System.Drawing.Size(112, 25); + this.apply.TabIndex = 1; + this.apply.TabStop = false; + this.apply.Text = "OK"; + this.apply.UseVisualStyleBackColor = true; + this.apply.Click += new System.EventHandler(this.apply_Click); + // + // textbox + // + this.textbox.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.textbox.Location = new System.Drawing.Point(10, 10); + this.textbox.Margin = new System.Windows.Forms.Padding(1); + this.textbox.Multiline = true; + this.textbox.Name = "textbox"; + this.textbox.ScrollBars = System.Windows.Forms.ScrollBars.Vertical; + this.textbox.Size = new System.Drawing.Size(456, 194); + this.textbox.TabIndex = 0; + this.textbox.TabStop = false; + // + // TextEditForm + // + this.AcceptButton = this.apply; + this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; + this.CancelButton = this.cancel; + this.ClientSize = new System.Drawing.Size(476, 255); + this.Controls.Add(this.textbox); + this.Controls.Add(this.cancel); + this.Controls.Add(this.apply); + this.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "TextEditForm"; + this.Opacity = 0; + this.ShowIcon = false; + this.ShowInTaskbar = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Edit Text"; + this.Activated += new System.EventHandler(this.TextEditForm_Activated); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Button cancel; + private System.Windows.Forms.Button apply; + private System.Windows.Forms.TextBox textbox; + } +} \ No newline at end of file diff --git a/Source/Core/Windows/TextEditForm.cs b/Source/Core/Windows/TextEditForm.cs new file mode 100644 index 0000000..25ef5b4 --- /dev/null +++ b/Source/Core/Windows/TextEditForm.cs @@ -0,0 +1,82 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Text; +using System.Windows.Forms; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Data; +using CodeImp.DoomBuilder.IO; +using System.IO; +using CodeImp.DoomBuilder.Config; +using CodeImp.DoomBuilder.Editing; +using CodeImp.DoomBuilder.Controls; + +#endregion + +namespace CodeImp.DoomBuilder.Windows +{ + public partial class TextEditForm : DelayedForm + { + // Properties + public string Value { get { return textbox.Text; } set { textbox.Text = value; } } + + // Constructor + public TextEditForm() + { + // Initialize + InitializeComponent(); + } + + // This shows the dialog, returns the same value when cancelled + public static string ShowDialog(IWin32Window owner, string value) + { + TextEditForm f = new TextEditForm(); + f.Value = value; + if (f.ShowDialog(owner) == DialogResult.OK) value = f.Value; + f.Dispose(); + return value; + } + + // OK clicked + private void apply_Click(object sender, EventArgs e) + { + // Done + this.DialogResult = DialogResult.OK; + this.Close(); + } + + // Cancel clciked + private void cancel_Click(object sender, EventArgs e) + { + // Be gone + this.DialogResult = DialogResult.Cancel; + this.Close(); + } + + // Window activated + private void TextEditForm_Activated(object sender, EventArgs e) + { + // Focus to textbox + textbox.Focus(); + } + } +} \ No newline at end of file diff --git a/Source/Core/Windows/TextEditForm.resx b/Source/Core/Windows/TextEditForm.resx new file mode 100644 index 0000000..528e887 --- /dev/null +++ b/Source/Core/Windows/TextEditForm.resx @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + True + + + True + + + True + + + True + + \ No newline at end of file diff --git a/Source/Core/Windows/TextureBrowserForm.Designer.cs b/Source/Core/Windows/TextureBrowserForm.Designer.cs new file mode 100644 index 0000000..142ce87 --- /dev/null +++ b/Source/Core/Windows/TextureBrowserForm.Designer.cs @@ -0,0 +1,167 @@ +namespace CodeImp.DoomBuilder.Windows +{ + partial class TextureBrowserForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(TextureBrowserForm)); + this.cancel = new System.Windows.Forms.Button(); + this.apply = new System.Windows.Forms.Button(); + this.texturesets = new System.Windows.Forms.ListView(); + this.namecolumn = new System.Windows.Forms.ColumnHeader(); + this.countcolumn = new System.Windows.Forms.ColumnHeader(); + this.smallimages = new System.Windows.Forms.ImageList(this.components); + this.browser = new CodeImp.DoomBuilder.Controls.ImageBrowserControl(); + this.SuspendLayout(); + // + // cancel + // + this.cancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.cancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.cancel.Location = new System.Drawing.Point(781, 596); + this.cancel.Name = "cancel"; + this.cancel.Size = new System.Drawing.Size(100, 25); + this.cancel.TabIndex = 3; + this.cancel.TabStop = false; + this.cancel.Text = "Cancel"; + this.cancel.UseVisualStyleBackColor = true; + this.cancel.Click += new System.EventHandler(this.cancel_Click); + // + // apply + // + this.apply.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.apply.Location = new System.Drawing.Point(675, 596); + this.apply.Name = "apply"; + this.apply.Size = new System.Drawing.Size(100, 25); + this.apply.TabIndex = 2; + this.apply.TabStop = false; + this.apply.Text = "OK"; + this.apply.UseVisualStyleBackColor = true; + this.apply.Click += new System.EventHandler(this.apply_Click); + // + // texturesets + // + this.texturesets.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left))); + this.texturesets.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.namecolumn, + this.countcolumn}); + this.texturesets.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.texturesets.FullRowSelect = true; + this.texturesets.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.None; + this.texturesets.HideSelection = false; + this.texturesets.Location = new System.Drawing.Point(12, 9); + this.texturesets.MultiSelect = false; + this.texturesets.Name = "texturesets"; + this.texturesets.Size = new System.Drawing.Size(200, 576); + this.texturesets.SmallImageList = this.smallimages; + this.texturesets.TabIndex = 0; + this.texturesets.TabStop = false; + this.texturesets.UseCompatibleStateImageBehavior = false; + this.texturesets.View = System.Windows.Forms.View.Details; + this.texturesets.SelectedIndexChanged += new System.EventHandler(this.texturesets_SelectedIndexChanged); + // + // namecolumn + // + this.namecolumn.Text = "Name"; + this.namecolumn.Width = 109; + // + // countcolumn + // + this.countcolumn.Text = "Count"; + this.countcolumn.TextAlign = System.Windows.Forms.HorizontalAlignment.Right; + this.countcolumn.Width = 51; + // + // smallimages + // + this.smallimages.ImageStream = ((System.Windows.Forms.ImageListStreamer)(resources.GetObject("smallimages.ImageStream"))); + this.smallimages.TransparentColor = System.Drawing.Color.Transparent; + this.smallimages.Images.SetKeyName(0, "KnownTextureSet2.ico"); + this.smallimages.Images.SetKeyName(1, "AllTextureSet2.ico"); + this.smallimages.Images.SetKeyName(2, "FileTextureSet.ico"); + this.smallimages.Images.SetKeyName(3, "FolderTextureSet.ico"); + this.smallimages.Images.SetKeyName(4, "PK3TextureSet.ico"); + // + // browser + // + this.browser.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.browser.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.browser.HideInputBox = false; + this.browser.LabelText = "Select or enter a texture name:"; + this.browser.Location = new System.Drawing.Point(218, 9); + this.browser.Name = "browser"; + this.browser.PreventSelection = false; + this.browser.Size = new System.Drawing.Size(663, 610); + this.browser.TabIndex = 1; + this.browser.TabStop = false; + this.browser.SelectedItemDoubleClicked += new CodeImp.DoomBuilder.Controls.ImageBrowserControl.SelectedItemDoubleClickDelegate(this.browser_SelectedItemDoubleClicked); + this.browser.SelectedItemChanged += new CodeImp.DoomBuilder.Controls.ImageBrowserControl.SelectedItemChangedDelegate(this.browser_SelectedItemChanged); + // + // TextureBrowserForm + // + this.AcceptButton = this.apply; + this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; + this.CancelButton = this.cancel; + this.ClientSize = new System.Drawing.Size(893, 631); + this.Controls.Add(this.texturesets); + this.Controls.Add(this.cancel); + this.Controls.Add(this.apply); + this.Controls.Add(this.browser); + this.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.KeyPreview = true; + this.MinimizeBox = false; + this.Name = "TextureBrowserForm"; + this.ShowIcon = false; + this.ShowInTaskbar = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.Manual; + this.Text = "Browse Textures"; + this.Load += new System.EventHandler(this.TextureBrowserForm_Load); + this.Shown += new System.EventHandler(this.TextureBrowserForm_Shown); + this.Activated += new System.EventHandler(this.TextureBrowserForm_Activated); + this.Move += new System.EventHandler(this.TextureBrowserForm_Move); + this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.TextureBrowserForm_FormClosing); + this.HelpRequested += new System.Windows.Forms.HelpEventHandler(this.TextureBrowserForm_HelpRequested); + this.ResizeEnd += new System.EventHandler(this.TextureBrowserForm_ResizeEnd); + this.ResumeLayout(false); + + } + + #endregion + + private CodeImp.DoomBuilder.Controls.ImageBrowserControl browser; + private System.Windows.Forms.Button cancel; + private System.Windows.Forms.Button apply; + private System.Windows.Forms.ListView texturesets; + private System.Windows.Forms.ColumnHeader namecolumn; + private System.Windows.Forms.ImageList smallimages; + private System.Windows.Forms.ColumnHeader countcolumn; + } +} \ No newline at end of file diff --git a/Source/Core/Windows/TextureBrowserForm.cs b/Source/Core/Windows/TextureBrowserForm.cs new file mode 100644 index 0000000..98d883e --- /dev/null +++ b/Source/Core/Windows/TextureBrowserForm.cs @@ -0,0 +1,360 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Text; +using System.Windows.Forms; +using CodeImp.DoomBuilder.IO; +using Microsoft.Win32; +using System.Diagnostics; +using CodeImp.DoomBuilder.Actions; +using CodeImp.DoomBuilder.Data; +using CodeImp.DoomBuilder.Config; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Controls; + +#endregion + +namespace CodeImp.DoomBuilder.Windows +{ + internal partial class TextureBrowserForm : Form + { + // Constants + private const int COLUMN_WIDTH_COUNT = 52; + + // Variables + private string selectedname; + private Point lastposition; + private Size lastsize; + private ListViewGroup usedgroup; + private ListViewGroup availgroup; + private ListViewItem selectedset; + private string selecttextureonfill; + + // Properties + public string SelectedName { get { return selectedname; } } + + // Constructor + public TextureBrowserForm(string selecttexture) + { + Cursor.Current = Cursors.WaitCursor; + ListViewItem item; + bool foundselecttexture = false; + long longname = Lump.MakeLongName(selecttexture ?? ""); + + // Initialize + InitializeComponent(); + browser.ApplySettings(); + + // Update the used textures + General.Map.Data.UpdateUsedTextures(); + + // Resize columns to maximize available width + countcolumn.Width = COLUMN_WIDTH_COUNT; + namecolumn.Width = texturesets.ClientRectangle.Width - SystemInformation.VerticalScrollBarWidth - countcolumn.Width - 2; + + // Fill texture sets list with normal texture sets + foreach (IFilledTextureSet ts in General.Map.Data.TextureSets) + { + item = texturesets.Items.Add(ts.Name); + item.Tag = ts; + item.ImageIndex = 0; + item.UseItemStyleForSubItems = false; + item.SubItems.Add(ts.Textures.Count.ToString(), item.ForeColor, + item.BackColor, new Font(item.Font, FontStyle.Regular)); + } + + // Add container-specific texture sets + foreach (ResourceTextureSet ts in General.Map.Data.ResourceTextureSets) + { + item = texturesets.Items.Add(ts.Name); + item.Tag = ts; + item.ImageIndex = 2 + ts.Location.type; + item.UseItemStyleForSubItems = false; + item.SubItems.Add(ts.Textures.Count.ToString(), item.ForeColor, + item.BackColor, new Font(item.Font, FontStyle.Regular)); + } + + // Add All textures set + item = texturesets.Items.Add(General.Map.Data.AllTextureSet.Name); + item.Tag = General.Map.Data.AllTextureSet; + item.ImageIndex = 1; + item.UseItemStyleForSubItems = false; + item.SubItems.Add(General.Map.Data.AllTextureSet.Textures.Count.ToString(), + item.ForeColor, item.BackColor, new Font(item.Font, FontStyle.Regular)); + + // Select the last one that was selected + string selectname = General.Settings.ReadSetting("browserwindow.textureset", ""); + foreach (ListViewItem i in texturesets.Items) + { + if (i.Text == selectname) + { + IFilledTextureSet set = (i.Tag as IFilledTextureSet); + foreach (ImageData img in set.Textures) + { + if (img.LongName == longname) + { + i.Selected = true; + foundselecttexture = true; + break; + } + } + break; + } + } + + // If the selected texture was not found in the last-selected set, try finding it in the other sets + if (!foundselecttexture) + { + foreach (ListViewItem i in texturesets.Items) + { + IFilledTextureSet set = (i.Tag as IFilledTextureSet); + foreach (ImageData img in set.Textures) + { + if (img.LongName == longname) + { + i.Selected = true; + foundselecttexture = true; + break; + } + } + if (foundselecttexture) break; + } + } + + // Texture still now found? Then just select the last used set + if (!foundselecttexture) + { + foreach (ListViewItem i in texturesets.Items) + { + if (i.Text == selectname) + { + i.Selected = true; + foundselecttexture = true; + break; + } + } + } + + // WARNING: Some strange behavior of the listview here! + // When you leave this line out, the list becomes very slow. + // Also, this does not change the item selected previously. + texturesets.Items[0].Selected = true; + + // Texture to select when list is filled + selecttextureonfill = selecttexture; + + // Make groups + usedgroup = browser.AddGroup("Used Textures"); + availgroup = browser.AddGroup("Available Textures"); + + // Keep last position and size + lastposition = this.Location; + lastsize = this.Size; + + // Position window from configuration settings + this.SuspendLayout(); + /* + this.Location = new Point(General.Settings.ReadSetting("browserwindow.positionx", this.Location.X), + General.Settings.ReadSetting("browserwindow.positiony", this.Location.Y)); + */ + this.Size = new Size(General.Settings.ReadSetting("browserwindow.sizewidth", this.Size.Width), + General.Settings.ReadSetting("browserwindow.sizeheight", this.Size.Height)); + this.WindowState = (FormWindowState)General.Settings.ReadSetting("browserwindow.windowstate", (int)FormWindowState.Normal); + if (this.WindowState == FormWindowState.Normal) this.StartPosition = FormStartPosition.CenterParent; + this.ResumeLayout(true); + } + + // Selection changed + private void browser_SelectedItemChanged() + { + apply.Enabled = (browser.SelectedItem != null); + } + + // OK clicked + private void apply_Click(object sender, EventArgs e) + { + // Set selected name and close + if (browser.SelectedItem != null) + { + selectedname = browser.SelectedItem.Text; + DialogResult = DialogResult.OK; + } + else + { + selectedname = ""; + DialogResult = DialogResult.Cancel; + } + this.Close(); + } + + // Cancel clicked + private void cancel_Click(object sender, EventArgs e) + { + // No selection, close + selectedname = ""; + DialogResult = DialogResult.Cancel; + this.Close(); + } + + // Activated + private void TextureBrowserForm_Activated(object sender, EventArgs e) + { + // Focus the textbox + browser.FocusTextbox(); + Cursor.Current = Cursors.Default; + } + + // Loading + private void TextureBrowserForm_Load(object sender, EventArgs e) + { + // Normal windowstate? + if (this.WindowState == FormWindowState.Normal) + { + // Keep last position and size + lastposition = this.Location; + lastsize = this.Size; + } + } + + // Resized + private void TextureBrowserForm_ResizeEnd(object sender, EventArgs e) + { + // Normal windowstate? + if (this.WindowState == FormWindowState.Normal) + { + // Keep last position and size + lastposition = this.Location; + lastsize = this.Size; + } + } + + // Moved + private void TextureBrowserForm_Move(object sender, EventArgs e) + { + // Normal windowstate? + if (this.WindowState == FormWindowState.Normal) + { + // Keep last position and size + lastposition = this.Location; + lastsize = this.Size; + } + } + + // Closing + private void TextureBrowserForm_FormClosing(object sender, FormClosingEventArgs e) + { + int windowstate; + + // Determine window state to save + if (this.WindowState != FormWindowState.Minimized) + windowstate = (int)this.WindowState; + else + windowstate = (int)FormWindowState.Normal; + + // Save window settings + General.Settings.WriteSetting("browserwindow.positionx", lastposition.X); + General.Settings.WriteSetting("browserwindow.positiony", lastposition.Y); + General.Settings.WriteSetting("browserwindow.sizewidth", lastsize.Width); + General.Settings.WriteSetting("browserwindow.sizeheight", lastsize.Height); + General.Settings.WriteSetting("browserwindow.windowstate", windowstate); + + // Save last selected texture set + if (texturesets.SelectedItems.Count > 0) + General.Settings.WriteSetting("browserwindow.textureset", texturesets.SelectedItems[0].Text); + + // Clean up + browser.CleanUp(); + } + + // Static method to browse for texture + // Returns null when cancelled. + public static string Browse(IWin32Window parent, string select) + { + TextureBrowserForm browser = new TextureBrowserForm(select); + if (browser.ShowDialog(parent) == DialogResult.OK) + { + // Return result + return browser.SelectedName; + } + else + { + // Cancelled + return select; + } + } + + // Texture set selected + private void texturesets_SelectedIndexChanged(object sender, EventArgs e) + { + // Anything slected? + if (texturesets.SelectedItems.Count > 0) + { + selectedset = texturesets.SelectedItems[0]; + FillImagesList(); + } + } + + // Item double clicked + private void browser_SelectedItemDoubleClicked() + { + if (apply.Enabled) apply_Click(this, EventArgs.Empty); + } + + // This fills the list of textures, depending on the selected texture set + private void FillImagesList() + { + // Get the selected texture set + IFilledTextureSet set = (selectedset.Tag as IFilledTextureSet); + + // Start adding + browser.BeginAdding(false); + + // Add all available textures and mark the images for temporary loading + foreach (ImageData img in set.Textures) + browser.Add(img.Name, img, img, availgroup); + + // Add all used textures and mark the images for permanent loading + foreach (ImageData img in set.Textures) + if (img.UsedInMap) browser.Add(img.Name, img, img, usedgroup); + + // Done adding + browser.EndAdding(); + } + + // Help + private void TextureBrowserForm_HelpRequested(object sender, HelpEventArgs hlpevent) + { + General.ShowHelp("w_imagesbrowser.html"); + hlpevent.Handled = true; + } + + private void TextureBrowserForm_Shown(object sender, EventArgs e) + { + // Select texture + if (!string.IsNullOrEmpty(selecttextureonfill)) + { + browser.SelectItem(selecttextureonfill, usedgroup); + selecttextureonfill = null; + } + } + } +} \ No newline at end of file diff --git a/Source/Core/Windows/TextureBrowserForm.resx b/Source/Core/Windows/TextureBrowserForm.resx new file mode 100644 index 0000000..5a00251 --- /dev/null +++ b/Source/Core/Windows/TextureBrowserForm.resx @@ -0,0 +1,199 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + True + + + True + + + True + + + 17, 17 + + + + AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj0yLjAuMC4w + LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAACZTeXN0 + ZW0uV2luZG93cy5Gb3Jtcy5JbWFnZUxpc3RTdHJlYW1lcgEAAAAERGF0YQcCAgAAAAkDAAAADwMAAAB+ + DAAAAk1TRnQBSQFMAgEBBQEAAQkBAAEEAQABEAEAARYBAAT/AQkBAAj/AUIBTQE2AQQGAAE2AQQCAAEo + AwABQAMAASwDAAEBAQABCAYAAQsYAAGAAgABgAMAAoABAAGAAwABgAEAAYABAAKAAgADwAEAAcAB3AHA + AQAB8AHKAaYBAAEzBQABMwEAATMBAAEzAQACMwIAAxYBAAMcAQADIgEAAykBAANVAQADTQEAA0IBAAM5 + AQABgAF8Af8BAAJQAf8BAAGTAQAB1gEAAf8B7AHMAQABxgHWAe8BAAHWAucBAAGQAakBrQIAAf8BMwMA + AWYDAAGZAwABzAIAATMDAAIzAgABMwFmAgABMwGZAgABMwHMAgABMwH/AgABZgMAAWYBMwIAAmYCAAFm + AZkCAAFmAcwCAAFmAf8CAAGZAwABmQEzAgABmQFmAgACmQIAAZkBzAIAAZkB/wIAAcwDAAHMATMCAAHM + AWYCAAHMAZkCAALMAgABzAH/AgAB/wFmAgAB/wGZAgAB/wHMAQABMwH/AgAB/wEAATMBAAEzAQABZgEA + ATMBAAGZAQABMwEAAcwBAAEzAQAB/wEAAf8BMwIAAzMBAAIzAWYBAAIzAZkBAAIzAcwBAAIzAf8BAAEz + AWYCAAEzAWYBMwEAATMCZgEAATMBZgGZAQABMwFmAcwBAAEzAWYB/wEAATMBmQIAATMBmQEzAQABMwGZ + AWYBAAEzApkBAAEzAZkBzAEAATMBmQH/AQABMwHMAgABMwHMATMBAAEzAcwBZgEAATMBzAGZAQABMwLM + AQABMwHMAf8BAAEzAf8BMwEAATMB/wFmAQABMwH/AZkBAAEzAf8BzAEAATMC/wEAAWYDAAFmAQABMwEA + AWYBAAFmAQABZgEAAZkBAAFmAQABzAEAAWYBAAH/AQABZgEzAgABZgIzAQABZgEzAWYBAAFmATMBmQEA + AWYBMwHMAQABZgEzAf8BAAJmAgACZgEzAQADZgEAAmYBmQEAAmYBzAEAAWYBmQIAAWYBmQEzAQABZgGZ + AWYBAAFmApkBAAFmAZkBzAEAAWYBmQH/AQABZgHMAgABZgHMATMBAAFmAcwBmQEAAWYCzAEAAWYBzAH/ + AQABZgH/AgABZgH/ATMBAAFmAf8BmQEAAWYB/wHMAQABzAEAAf8BAAH/AQABzAEAApkCAAGZATMBmQEA + AZkBAAGZAQABmQEAAcwBAAGZAwABmQIzAQABmQEAAWYBAAGZATMBzAEAAZkBAAH/AQABmQFmAgABmQFm + ATMBAAGZATMBZgEAAZkBZgGZAQABmQFmAcwBAAGZATMB/wEAApkBMwEAApkBZgEAA5kBAAKZAcwBAAKZ + Af8BAAGZAcwCAAGZAcwBMwEAAWYBzAFmAQABmQHMAZkBAAGZAswBAAGZAcwB/wEAAZkB/wIAAZkB/wEz + AQABmQHMAWYBAAGZAf8BmQEAAZkB/wHMAQABmQL/AQABzAMAAZkBAAEzAQABzAEAAWYBAAHMAQABmQEA + AcwBAAHMAQABmQEzAgABzAIzAQABzAEzAWYBAAHMATMBmQEAAcwBMwHMAQABzAEzAf8BAAHMAWYCAAHM + AWYBMwEAAZkCZgEAAcwBZgGZAQABzAFmAcwBAAGZAWYB/wEAAcwBmQIAAcwBmQEzAQABzAGZAWYBAAHM + ApkBAAHMAZkBzAEAAcwBmQH/AQACzAIAAswBMwEAAswBZgEAAswBmQEAA8wBAALMAf8BAAHMAf8CAAHM + Af8BMwEAAZkB/wFmAQABzAH/AZkBAAHMAf8BzAEAAcwC/wEAAcwBAAEzAQAB/wEAAWYBAAH/AQABmQEA + AcwBMwIAAf8CMwEAAf8BMwFmAQAB/wEzAZkBAAH/ATMBzAEAAf8BMwH/AQAB/wFmAgAB/wFmATMBAAHM + AmYBAAH/AWYBmQEAAf8BZgHMAQABzAFmAf8BAAH/AZkCAAH/AZkBMwEAAf8BmQFmAQAB/wKZAQAB/wGZ + AcwBAAH/AZkB/wEAAf8BzAIAAf8BzAEzAQAB/wHMAWYBAAH/AcwBmQEAAf8CzAEAAf8BzAH/AQAC/wEz + AQABzAH/AWYBAAL/AZkBAAL/AcwBAAJmAf8BAAFmAf8BZgEAAWYC/wEAAf8CZgEAAf8BZgH/AQAC/wFm + AQABIQEAAaUBAANfAQADdwEAA4YBAAOWAQADywEAA7IBAAPXAQAD3QEAA+MBAAPqAQAD8QEAA/gBAAHw + AfsB/wEAAaQCoAEAA4ADAAH/AgAB/wMAAv8BAAH/AwAB/wEAAf8BAAL/AgAD//8ABQAHDgEQAW0BbAIO + MwABDgENAWwBkAG7AgkB9wJsAWYCDjsAAQ4B6gFsAQ8BDjQAAW0HAAFDAW0BDjQAAW0B8QFtAQcBvAHw + AfECAAERAQcB7DQAA20BBwK8AfACAAJtAQ40AAFtAfADBwK8AgABEQEHAew0AAFtAfAB7wMHAbwCAAJt + AQ40AAFtAfAC7wMHAgABEQEHAew0AAFtAfAD7wIHAgACbQEONAABbQG8BO8BBwIAAREBBwHsNAAIbQEA + AQ8B7AEPNAAJDgHrAZEBDTQAAQ4BZQGQAbsFCQH3AWwCDj4AAQ7/AMMAAfcCrgFtAWwB6gJsBGY0AAH3 + Bf8C9AMZAWYJAAgOEwAB9wKuAW0BbAHqAmwEZgQAAbUC/wKTAZkBBwG7AbQCGQNmBgABDgEABe8B9wEO + AwACcwHrAW4BbQFKARMBFQFDAREBDwEOAQ8DAAH3Bf8C9AMZAWYEAAG1Av8BkwGaAXoBnwGeAbsB9AHz + AWYBGQFmBQABDgHvAQAB8ALxAvIBvAEOAwABcwIcBzECSwFEAe0CAAG1Av8CkwGZAQcBuwG0AhkDZgIA + AbUC/wG7AQgCnwF+AXkC9AFsARkDZgIAAQ4B7wEHAQAB8ALxAvIB8AEOAwAB7AF6ARwBmgN6AlkBUwFS + ATEBSwHqAgABtQL/AZMBmgF6AZ8BngG7AfQB8wFmARkBZgIAAbUC/wLbAQkBCAF+AXkB/wH0AWwB8wFm + ARkBZgIAAQ4CAAHvAbwB8ALxAfIB8AEOAwAB7QF6ARwBmQV6AlkBUwExAUQB7QEAAbUC/wG7AQgCnwF+ + AXkC9AFsARkDZgEHCv8BbAH0AWwBGQFmAgABDgHwAgcCvALwAfEBvAEOAwAB7QGaAXoBHAGaBXoDWQFL + AW0BAAG1Av8C2wEJAQgBfgF5Af8B9AFsAfMBZgEZAWYCBwS1AfcBtQLtApEB9AFsAfMBZgIAAQ4B8AMH + ArwC8AG8AQ4DAAEcAZoBegEcAZkHegFZAVIBSgHtAQcK/wFsAfQBbAEZAWYBtQH/AQcK/wFsAfQBbAIA + AQ4B8AQHArwB8AG8AQ4DAAEcAZoBoAF6ARwHmgJ6AXQBbQIHBLUB9wG1Au0CkQH0AWwB8wFmAbUB/wIH + BLUB9wG1Au0CkQH0AWwCAAEOAfAC7wQHAbwBBwEOAwABHAOgCBwC7QHsAXMCAAEHCv8BbAH0AWwBBwP/ + AQcK/wFsAgABDgHwA+8FBwEOAwABHASgAZoGegFzBQACBwS1AfcBtQLtApEB9AFsAgcCtQIHBLUB9wG1 + Au0CkQIAAQ4BvATvBAcBDgMAARwBoAHDA6ABegMcAu0BcwcAAQcK/wFsAgABBwr/AWwB9AFsAgABDgG8 + Be8DBwEOAwABHAGZAsMCoAEcAe0MAAIHBLUB9wG1Au0CkQIAAgcEtQH3AbUC7QKRAfQBbAIAAQ4B9APw + A7wCBwEOBAAFHAHtHQABBwr/AWwCAAsOJwACBwS1AfcBtQLtApHgAAFCAU0BPgcAAT4DAAEoAwABQAMA + ASwDAAEBAQABAQUAAWABARYAA/8BAAL/BgAC/wYAAv8GAAH/AcMGAAHgAQEGAAHAAQEGAAGAAQEGAAHg + ASMGAAHAASMGAAHAASMGAAHAASMGAAHAASMGAAHAASMGAAHAASMGAAHAASMGAAHAASMGAAGAAQEGAAHA + AQEGAAHgAQMGAAL/BgAC/wYAAv8GABr/AQABDwb/AQABDwH4AQcC/wEAAQ8BAAEDAfABBwEAAQcBAAEP + AQABAwHgAQcBAAEDAQABAwIAAcABBwEAAQMBAAEDAgABwAEHAQABAQQAAcABBwEAAQEEAAHAAQcGAAHA + AQcGAAHAAQcCAAHAAwABwAEHAQABBwHAAwABwAEHAQABBwHwAQABwAEAAcABBwEAAf8B8AEAAcABAAHA + AQcBgQP/AfABAAHAAQcE/wHwAQAc/ws= + + + + True + + + True + + \ No newline at end of file diff --git a/Source/Core/Windows/TextureSetForm.Designer.cs b/Source/Core/Windows/TextureSetForm.Designer.cs new file mode 100644 index 0000000..5b381e5 --- /dev/null +++ b/Source/Core/Windows/TextureSetForm.Designer.cs @@ -0,0 +1,326 @@ +namespace CodeImp.DoomBuilder.Windows +{ + partial class TextureSetForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + this.label1 = new System.Windows.Forms.Label(); + this.name = new System.Windows.Forms.TextBox(); + this.filters = new System.Windows.Forms.ListView(); + this.filtercolumn = new System.Windows.Forms.ColumnHeader(); + this.label2 = new System.Windows.Forms.Label(); + this.label3 = new System.Windows.Forms.Label(); + this.label4 = new System.Windows.Forms.Label(); + this.apply = new System.Windows.Forms.Button(); + this.cancel = new System.Windows.Forms.Button(); + this.addfilter = new System.Windows.Forms.Button(); + this.removefilter = new System.Windows.Forms.Button(); + this.groupBox1 = new System.Windows.Forms.GroupBox(); + this.filterstimer = new System.Windows.Forms.Timer(this.components); + this.groupBox2 = new System.Windows.Forms.GroupBox(); + this.nomatchesbutton = new System.Windows.Forms.RadioButton(); + this.matchesbutton = new System.Windows.Forms.RadioButton(); + this.matcheslist = new CodeImp.DoomBuilder.Controls.ImageBrowserControl(); + this.noresultlabel = new System.Windows.Forms.Label(); + this.pictureBox1 = new System.Windows.Forms.PictureBox(); + this.groupBox1.SuspendLayout(); + this.groupBox2.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit(); + this.SuspendLayout(); + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(36, 24); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(37, 14); + this.label1.TabIndex = 0; + this.label1.Text = "Name:"; + // + // name + // + this.name.Location = new System.Drawing.Point(79, 21); + this.name.Name = "name"; + this.name.Size = new System.Drawing.Size(173, 20); + this.name.TabIndex = 0; + // + // filters + // + this.filters.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.filters.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.filtercolumn}); + this.filters.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.None; + this.filters.HideSelection = false; + this.filters.LabelEdit = true; + this.filters.Location = new System.Drawing.Point(21, 110); + this.filters.Name = "filters"; + this.filters.ShowGroups = false; + this.filters.Size = new System.Drawing.Size(219, 280); + this.filters.TabIndex = 0; + this.filters.UseCompatibleStateImageBehavior = false; + this.filters.View = System.Windows.Forms.View.Details; + this.filters.AfterLabelEdit += new System.Windows.Forms.LabelEditEventHandler(this.filters_AfterLabelEdit); + this.filters.SelectedIndexChanged += new System.EventHandler(this.filters_SelectedIndexChanged); + this.filters.DoubleClick += new System.EventHandler(this.filters_DoubleClick); + // + // filtercolumn + // + this.filtercolumn.Text = "Filter"; + this.filtercolumn.Width = 192; + // + // label2 + // + this.label2.Location = new System.Drawing.Point(18, 28); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(248, 42); + this.label2.TabIndex = 3; + this.label2.Text = "Add the names of the textures in this set below. You can use the following wildca" + + "rds:"; + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Location = new System.Drawing.Point(28, 65); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(175, 14); + this.label3.TabIndex = 4; + this.label3.Text = "? = matches exactly one character"; + // + // label4 + // + this.label4.AutoSize = true; + this.label4.Location = new System.Drawing.Point(28, 83); + this.label4.Name = "label4"; + this.label4.Size = new System.Drawing.Size(185, 14); + this.label4.TabIndex = 5; + this.label4.Text = "* = matches zero or more characters"; + // + // apply + // + this.apply.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.apply.Location = new System.Drawing.Point(503, 515); + this.apply.Name = "apply"; + this.apply.Size = new System.Drawing.Size(105, 25); + this.apply.TabIndex = 3; + this.apply.Text = "OK"; + this.apply.UseVisualStyleBackColor = true; + this.apply.Click += new System.EventHandler(this.apply_Click); + // + // cancel + // + this.cancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.cancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.cancel.Location = new System.Drawing.Point(614, 515); + this.cancel.Name = "cancel"; + this.cancel.Size = new System.Drawing.Size(105, 25); + this.cancel.TabIndex = 4; + this.cancel.Text = "Cancel"; + this.cancel.UseVisualStyleBackColor = true; + this.cancel.Click += new System.EventHandler(this.cancel_Click); + // + // addfilter + // + this.addfilter.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.addfilter.Location = new System.Drawing.Point(21, 396); + this.addfilter.Name = "addfilter"; + this.addfilter.Size = new System.Drawing.Size(97, 24); + this.addfilter.TabIndex = 1; + this.addfilter.Text = "Add Texture"; + this.addfilter.UseVisualStyleBackColor = true; + this.addfilter.Click += new System.EventHandler(this.addfilter_Click); + // + // removefilter + // + this.removefilter.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.removefilter.Enabled = false; + this.removefilter.Location = new System.Drawing.Point(124, 396); + this.removefilter.Name = "removefilter"; + this.removefilter.Size = new System.Drawing.Size(105, 24); + this.removefilter.TabIndex = 2; + this.removefilter.Text = "Remove Selection"; + this.removefilter.UseVisualStyleBackColor = true; + this.removefilter.Click += new System.EventHandler(this.removefilter_Click); + // + // groupBox1 + // + this.groupBox1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left))); + this.groupBox1.Controls.Add(this.removefilter); + this.groupBox1.Controls.Add(this.label4); + this.groupBox1.Controls.Add(this.addfilter); + this.groupBox1.Controls.Add(this.label3); + this.groupBox1.Controls.Add(this.label2); + this.groupBox1.Controls.Add(this.filters); + this.groupBox1.Location = new System.Drawing.Point(12, 60); + this.groupBox1.Name = "groupBox1"; + this.groupBox1.Size = new System.Drawing.Size(270, 440); + this.groupBox1.TabIndex = 1; + this.groupBox1.TabStop = false; + this.groupBox1.Text = " Filters "; + // + // filterstimer + // + this.filterstimer.Interval = 1; + this.filterstimer.Tick += new System.EventHandler(this.filterstimer_Tick); + // + // groupBox2 + // + this.groupBox2.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.groupBox2.Controls.Add(this.nomatchesbutton); + this.groupBox2.Controls.Add(this.matchesbutton); + this.groupBox2.Controls.Add(this.matcheslist); + this.groupBox2.Controls.Add(this.noresultlabel); + this.groupBox2.Location = new System.Drawing.Point(298, 60); + this.groupBox2.Name = "groupBox2"; + this.groupBox2.Size = new System.Drawing.Size(421, 440); + this.groupBox2.TabIndex = 2; + this.groupBox2.TabStop = false; + this.groupBox2.Text = " Results "; + // + // nomatchesbutton + // + this.nomatchesbutton.Appearance = System.Windows.Forms.Appearance.Button; + this.nomatchesbutton.Location = new System.Drawing.Point(141, 25); + this.nomatchesbutton.Name = "nomatchesbutton"; + this.nomatchesbutton.Size = new System.Drawing.Size(117, 24); + this.nomatchesbutton.TabIndex = 1; + this.nomatchesbutton.Text = "Show Not Matching"; + this.nomatchesbutton.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; + this.nomatchesbutton.UseVisualStyleBackColor = true; + this.nomatchesbutton.Click += new System.EventHandler(this.matchesbutton_Click); + // + // matchesbutton + // + this.matchesbutton.Appearance = System.Windows.Forms.Appearance.Button; + this.matchesbutton.Checked = true; + this.matchesbutton.Location = new System.Drawing.Point(18, 25); + this.matchesbutton.Name = "matchesbutton"; + this.matchesbutton.Size = new System.Drawing.Size(117, 24); + this.matchesbutton.TabIndex = 0; + this.matchesbutton.TabStop = true; + this.matchesbutton.Text = "Show Matches"; + this.matchesbutton.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; + this.matchesbutton.UseVisualStyleBackColor = true; + this.matchesbutton.Click += new System.EventHandler(this.matchesbutton_Click); + // + // matcheslist + // + this.matcheslist.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.matcheslist.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.matcheslist.HideInputBox = true; + this.matcheslist.LabelText = "Select or type object name:"; + this.matcheslist.Location = new System.Drawing.Point(18, 55); + this.matcheslist.Name = "matcheslist"; + this.matcheslist.PreventSelection = true; + this.matcheslist.Size = new System.Drawing.Size(387, 365); + this.matcheslist.TabIndex = 2; + this.matcheslist.SelectedItemDoubleClicked += new CodeImp.DoomBuilder.Controls.ImageBrowserControl.SelectedItemDoubleClickDelegate(this.matcheslist_SelectedItemDoubleClicked); + // + // noresultlabel + // + this.noresultlabel.Location = new System.Drawing.Point(15, 28); + this.noresultlabel.Name = "noresultlabel"; + this.noresultlabel.Size = new System.Drawing.Size(272, 43); + this.noresultlabel.TabIndex = 33; + this.noresultlabel.Text = "An example result cannot be displayed, because it requires a map to be loaded."; + this.noresultlabel.Visible = false; + // + // pictureBox1 + // + this.pictureBox1.Image = global::CodeImp.DoomBuilder.Properties.Resources.KnownTextureSet; + this.pictureBox1.Location = new System.Drawing.Point(12, 23); + this.pictureBox1.Name = "pictureBox1"; + this.pictureBox1.Size = new System.Drawing.Size(19, 16); + this.pictureBox1.SizeMode = System.Windows.Forms.PictureBoxSizeMode.AutoSize; + this.pictureBox1.TabIndex = 12; + this.pictureBox1.TabStop = false; + // + // TextureSetForm + // + this.AcceptButton = this.apply; + this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; + this.CancelButton = this.cancel; + this.ClientSize = new System.Drawing.Size(731, 552); + this.Controls.Add(this.pictureBox1); + this.Controls.Add(this.groupBox2); + this.Controls.Add(this.cancel); + this.Controls.Add(this.apply); + this.Controls.Add(this.name); + this.Controls.Add(this.label1); + this.Controls.Add(this.groupBox1); + this.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "TextureSetForm"; + this.Opacity = 0; + this.ShowIcon = false; + this.ShowInTaskbar = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Edit Texture Set"; + this.Shown += new System.EventHandler(this.TextureSetForm_Shown); + this.HelpRequested += new System.Windows.Forms.HelpEventHandler(this.TextureSetForm_HelpRequested); + this.groupBox1.ResumeLayout(false); + this.groupBox1.PerformLayout(); + this.groupBox2.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Label label1; + private System.Windows.Forms.TextBox name; + private System.Windows.Forms.ListView filters; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.Label label3; + private System.Windows.Forms.Label label4; + private System.Windows.Forms.Button apply; + private System.Windows.Forms.Button cancel; + private System.Windows.Forms.ColumnHeader filtercolumn; + private System.Windows.Forms.Button addfilter; + private System.Windows.Forms.Button removefilter; + private System.Windows.Forms.GroupBox groupBox1; + private System.Windows.Forms.Timer filterstimer; + private System.Windows.Forms.GroupBox groupBox2; + private CodeImp.DoomBuilder.Controls.ImageBrowserControl matcheslist; + private System.Windows.Forms.Label noresultlabel; + private System.Windows.Forms.RadioButton nomatchesbutton; + private System.Windows.Forms.RadioButton matchesbutton; + private System.Windows.Forms.PictureBox pictureBox1; + } +} \ No newline at end of file diff --git a/Source/Core/Windows/TextureSetForm.cs b/Source/Core/Windows/TextureSetForm.cs new file mode 100644 index 0000000..1911dc4 --- /dev/null +++ b/Source/Core/Windows/TextureSetForm.cs @@ -0,0 +1,232 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Text; +using System.Windows.Forms; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Data; +using CodeImp.DoomBuilder.IO; +using System.IO; +using CodeImp.DoomBuilder.Config; +using CodeImp.DoomBuilder.Editing; +using CodeImp.DoomBuilder.Controls; + +#endregion + +namespace CodeImp.DoomBuilder.Windows +{ + internal partial class TextureSetForm : DelayedForm + { + // Variables + private DefinedTextureSet textureset; + + // Constructor + public TextureSetForm() + { + InitializeComponent(); + matcheslist.ApplySettings(); + + // Show/hide components + matchesbutton.Visible = (General.Map != null); + nomatchesbutton.Visible = (General.Map != null); + matcheslist.Visible = (General.Map != null); + noresultlabel.Visible = (General.Map == null); + } + + // This initializes the set + public void Setup(DefinedTextureSet set) + { + // Keep reference + textureset = set; + + // Set name + name.Text = set.Name; + + // Fill filters list + foreach (string s in set.Filters) + filters.Items.Add(s); + } + + // OK clicked + private void apply_Click(object sender, EventArgs e) + { + // Apply name + textureset.Name = name.Text; + + // Apply filters + textureset.Filters.Clear(); + foreach (ListViewItem i in filters.Items) textureset.Filters.Add(i.Text); + + // Done + matcheslist.CleanUp(); + this.DialogResult = DialogResult.OK; + this.Close(); + } + + // Cancel clicked + private void cancel_Click(object sender, EventArgs e) + { + // Be gone. + matcheslist.CleanUp(); + this.DialogResult = DialogResult.Cancel; + this.Close(); + } + + // Add texture + private void addfilter_Click(object sender, EventArgs e) + { + ListViewItem i = new ListViewItem(""); + filters.Items.Add(i); + i.BeginEdit(); + } + + // Remove selected items + private void removefilter_Click(object sender, EventArgs e) + { + foreach (ListViewItem i in filters.SelectedItems) i.Remove(); + + // Run the timer + filterstimer.Start(); + } + + // Items selected/deselected + private void filters_SelectedIndexChanged(object sender, EventArgs e) + { + removefilter.Enabled = (filters.SelectedItems.Count > 0); + } + + // Double clicking an item + private void filters_DoubleClick(object sender, EventArgs e) + { + // Edit item + if (filters.SelectedItems.Count == 1) + filters.SelectedItems[0].BeginEdit(); + } + + // This removes empty items and makes others uppercase + private void filterstimer_Tick(object sender, EventArgs e) + { + // Stop timer + filterstimer.Stop(); + + // Update labels + for (int i = filters.Items.Count - 1; i >= 0; i--) + { + // Empty label? + if ((filters.Items[i].Text == null) || + (filters.Items[i].Text.Trim().Length == 0)) + { + // Remove it + filters.Items.RemoveAt(i); + } + else + { + // Make uppercase + filters.Items[i].Text = filters.Items[i].Text.ToUpperInvariant(); + } + } + + // Show example results if when we can + if (General.Map != null) + { + Cursor.Current = Cursors.AppStarting; + + // Make a set for comparing + List filterslist = new List(filters.Items.Count); + foreach (ListViewItem i in filters.Items) filterslist.Add(i.Text); + MatchingTextureSet set = new MatchingTextureSet(filterslist); + + // Determine tooltip text + string tooltiptext = null; + if (nomatchesbutton.Checked) tooltiptext = "Doubleclick to include this texture"; + + // Start adding + matcheslist.PreventSelection = matchesbutton.Checked; + matcheslist.BeginAdding(true); + + // Go for all textures + foreach (ImageData img in General.Map.Data.Textures) + { + bool ismatch = set.IsMatch(img); + if ((ismatch && matchesbutton.Checked) || (!ismatch && nomatchesbutton.Checked)) + matcheslist.Add(img.Name, img, img, null, tooltiptext); + } + + // If not already mixed, add flats as well + if (!General.Map.Config.MixTexturesFlats) + { + // Go for all flats + foreach (ImageData img in General.Map.Data.Flats) + { + bool ismatch = set.IsMatch(img); + if ((ismatch && matchesbutton.Checked) || (!ismatch && nomatchesbutton.Checked)) + matcheslist.Add(img.Name, img, img, null, tooltiptext); + } + } + + // Done adding + matcheslist.EndAdding(); + Cursor.Current = Cursors.Default; + } + } + + // Done editing a filter + private void filters_AfterLabelEdit(object sender, LabelEditEventArgs e) + { + // Run the timer + filterstimer.Start(); + } + + // When first shown + private void TextureSetForm_Shown(object sender, EventArgs e) + { + // Run the timer + filterstimer.Start(); + } + + // Show (not) matches clicked + private void matchesbutton_Click(object sender, EventArgs e) + { + // Run the timer + filterstimer.Start(); + } + + // Texture doubleclicked + private void matcheslist_SelectedItemDoubleClicked() + { + // Add texture name to the list + if (matcheslist.SelectedItem != null) + filters.Items.Add(matcheslist.SelectedItem.Text); + + // Run the timer + filterstimer.Start(); + } + + // Help + private void TextureSetForm_HelpRequested(object sender, HelpEventArgs hlpevent) + { + General.ShowHelp("w_textureset.html"); + hlpevent.Handled = true; + } + } +} + diff --git a/Source/Core/Windows/TextureSetForm.resx b/Source/Core/Windows/TextureSetForm.resx new file mode 100644 index 0000000..be0d381 --- /dev/null +++ b/Source/Core/Windows/TextureSetForm.resx @@ -0,0 +1,177 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + 17, 17 + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + \ No newline at end of file diff --git a/Source/Core/Windows/ThingBrowserForm.Designer.cs b/Source/Core/Windows/ThingBrowserForm.Designer.cs new file mode 100644 index 0000000..f848546 --- /dev/null +++ b/Source/Core/Windows/ThingBrowserForm.Designer.cs @@ -0,0 +1,98 @@ +namespace CodeImp.DoomBuilder.Windows +{ + partial class ThingBrowserForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.thingslist = new CodeImp.DoomBuilder.Controls.ThingBrowserControl(); + this.cancel = new System.Windows.Forms.Button(); + this.apply = new System.Windows.Forms.Button(); + this.SuspendLayout(); + // + // thingslist + // + this.thingslist.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.thingslist.Location = new System.Drawing.Point(9, 12); + this.thingslist.Name = "thingslist"; + this.thingslist.Size = new System.Drawing.Size(394, 383); + this.thingslist.TabIndex = 0; + this.thingslist.OnTypeDoubleClicked += new CodeImp.DoomBuilder.Controls.ThingBrowserControl.TypeDoubleClickDeletegate(this.thingslist_OnTypeDoubleClicked); + // + // cancel + // + this.cancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.cancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.cancel.Location = new System.Drawing.Point(291, 416); + this.cancel.Name = "cancel"; + this.cancel.Size = new System.Drawing.Size(112, 27); + this.cancel.TabIndex = 2; + this.cancel.Text = "Cancel"; + this.cancel.UseVisualStyleBackColor = true; + this.cancel.Click += new System.EventHandler(this.cancel_Click); + // + // apply + // + this.apply.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.apply.Location = new System.Drawing.Point(171, 416); + this.apply.Name = "apply"; + this.apply.Size = new System.Drawing.Size(112, 27); + this.apply.TabIndex = 1; + this.apply.Text = "OK"; + this.apply.UseVisualStyleBackColor = true; + this.apply.Click += new System.EventHandler(this.apply_Click); + // + // ThingBrowserForm + // + this.AcceptButton = this.apply; + this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; + this.CancelButton = this.cancel; + this.ClientSize = new System.Drawing.Size(412, 453); + this.Controls.Add(this.cancel); + this.Controls.Add(this.apply); + this.Controls.Add(this.thingslist); + this.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "ThingBrowserForm"; + this.Opacity = 0; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Edit Thing"; + this.ResumeLayout(false); + + } + + #endregion + + private CodeImp.DoomBuilder.Controls.ThingBrowserControl thingslist; + private System.Windows.Forms.Button cancel; + private System.Windows.Forms.Button apply; + } +} \ No newline at end of file diff --git a/Source/Core/Windows/ThingBrowserForm.cs b/Source/Core/Windows/ThingBrowserForm.cs new file mode 100644 index 0000000..a0f9f64 --- /dev/null +++ b/Source/Core/Windows/ThingBrowserForm.cs @@ -0,0 +1,94 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Text; +using System.Windows.Forms; +using Microsoft.Win32; +using System.Diagnostics; +using CodeImp.DoomBuilder.Actions; +using CodeImp.DoomBuilder.Data; +using CodeImp.DoomBuilder.Config; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Controls; + +#endregion + +namespace CodeImp.DoomBuilder.Windows +{ + public partial class ThingBrowserForm : DelayedForm + { + // Variables + public int selectedtype; + + // Properties + public int SelectedType { get { return selectedtype; } } + + // Constructor + public ThingBrowserForm(int type) + { + InitializeComponent(); + + // Setup list + thingslist.Setup(); + + // Select given type + thingslist.SelectType(type); + } + + // This browses for a thing type + // Returns the new thing type or the same thing type when cancelled + public static int BrowseThing(IWin32Window owner, int type) + { + ThingBrowserForm f = new ThingBrowserForm(type); + if (f.ShowDialog(owner) == DialogResult.OK) type = f.SelectedType; + f.Dispose(); + return type; + } + + // OK clicked + private void apply_Click(object sender, EventArgs e) + { + // Get the result + selectedtype = thingslist.GetResult(selectedtype); + + // Done + this.DialogResult = DialogResult.OK; + this.Close(); + } + + // Cancel clicked + private void cancel_Click(object sender, EventArgs e) + { + // Leave + this.DialogResult = DialogResult.Cancel; + this.Close(); + } + + // Double-clicked an item + private void thingslist_OnTypeDoubleClicked() + { + // OK + apply_Click(this, EventArgs.Empty); + } + } +} \ No newline at end of file diff --git a/Source/Core/Windows/ThingBrowserForm.resx b/Source/Core/Windows/ThingBrowserForm.resx new file mode 100644 index 0000000..e797e68 --- /dev/null +++ b/Source/Core/Windows/ThingBrowserForm.resx @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + True + + + True + + + True + + + True + + \ No newline at end of file diff --git a/Source/Core/Windows/ThingEditForm.Designer.cs b/Source/Core/Windows/ThingEditForm.Designer.cs new file mode 100644 index 0000000..dcec8a2 --- /dev/null +++ b/Source/Core/Windows/ThingEditForm.Designer.cs @@ -0,0 +1,318 @@ +namespace CodeImp.DoomBuilder.Windows +{ + partial class ThingEditForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.Windows.Forms.GroupBox groupBox1; + System.Windows.Forms.GroupBox groupBox2; + System.Windows.Forms.Label label5; + System.Windows.Forms.Label taglabel; + this.thingtype = new CodeImp.DoomBuilder.Controls.ThingBrowserControl(); + this.height = new CodeImp.DoomBuilder.Controls.ButtonsNumericTextbox(); + this.angle = new CodeImp.DoomBuilder.Controls.ButtonsNumericTextbox(); + this.heightlabel = new System.Windows.Forms.Label(); + this.anglecontrol = new CodeImp.DoomBuilder.Controls.AngleControl(); + this.cancel = new System.Windows.Forms.Button(); + this.apply = new System.Windows.Forms.Button(); + this.thingproperties = new System.Windows.Forms.Panel(); + this.groupBox3 = new System.Windows.Forms.GroupBox(); + this.tag = new CodeImp.DoomBuilder.Controls.ButtonsNumericTextbox(); + this.newtag = new System.Windows.Forms.Button(); + this.spritetex = new System.Windows.Forms.Panel(); + this.settingsgroup = new System.Windows.Forms.GroupBox(); + this.flags = new CodeImp.DoomBuilder.Controls.CheckboxArrayControl(); + groupBox1 = new System.Windows.Forms.GroupBox(); + groupBox2 = new System.Windows.Forms.GroupBox(); + label5 = new System.Windows.Forms.Label(); + taglabel = new System.Windows.Forms.Label(); + groupBox1.SuspendLayout(); + groupBox2.SuspendLayout(); + this.thingproperties.SuspendLayout(); + this.groupBox3.SuspendLayout(); + this.settingsgroup.SuspendLayout(); + this.SuspendLayout(); + // + // groupBox1 + // + groupBox1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left))); + groupBox1.Controls.Add(this.thingtype); + groupBox1.Location = new System.Drawing.Point(6, 6); + groupBox1.Name = "groupBox1"; + groupBox1.Size = new System.Drawing.Size(269, 386); + groupBox1.TabIndex = 0; + groupBox1.TabStop = false; + groupBox1.Text = " Thing "; + // + // thingtype + // + this.thingtype.Location = new System.Drawing.Point(9, 22); + this.thingtype.Margin = new System.Windows.Forms.Padding(6); + this.thingtype.Name = "thingtype"; + this.thingtype.Size = new System.Drawing.Size(251, 364); + this.thingtype.TabIndex = 0; + this.thingtype.OnTypeChanged += new CodeImp.DoomBuilder.Controls.ThingBrowserControl.TypeChangedDeletegate(this.thingtype_OnTypeChanged); + // + // groupBox2 + // + groupBox2.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + groupBox2.Controls.Add(this.height); + groupBox2.Controls.Add(this.angle); + groupBox2.Controls.Add(this.heightlabel); + groupBox2.Controls.Add(label5); + groupBox2.Controls.Add(this.anglecontrol); + groupBox2.Location = new System.Drawing.Point(397, 287); + groupBox2.Name = "groupBox2"; + groupBox2.Size = new System.Drawing.Size(249, 105); + groupBox2.TabIndex = 2; + groupBox2.TabStop = false; + groupBox2.Text = " Coordination "; + // + // height + // + this.height.AllowDecimal = false; + this.height.AllowNegative = true; + this.height.AllowRelative = true; + this.height.ButtonStep = 8; + this.height.Location = new System.Drawing.Point(68, 61); + this.height.Name = "height"; + this.height.Size = new System.Drawing.Size(72, 24); + this.height.StepValues = null; + this.height.TabIndex = 11; + // + // angle + // + this.angle.AllowDecimal = false; + this.angle.AllowNegative = true; + this.angle.AllowRelative = true; + this.angle.ButtonStep = 45; + this.angle.Location = new System.Drawing.Point(68, 26); + this.angle.Name = "angle"; + this.angle.Size = new System.Drawing.Size(72, 24); + this.angle.StepValues = null; + this.angle.TabIndex = 10; + this.angle.WhenTextChanged += new System.EventHandler(this.angle_TextChanged); + // + // heightlabel + // + this.heightlabel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.heightlabel.AutoSize = true; + this.heightlabel.Location = new System.Drawing.Point(12, 66); + this.heightlabel.Name = "heightlabel"; + this.heightlabel.Size = new System.Drawing.Size(50, 14); + this.heightlabel.TabIndex = 9; + this.heightlabel.Text = "Z Height:"; + // + // label5 + // + label5.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + label5.AutoSize = true; + label5.Location = new System.Drawing.Point(24, 31); + label5.Name = "label5"; + label5.Size = new System.Drawing.Size(38, 14); + label5.TabIndex = 8; + label5.Text = "Angle:"; + // + // anglecontrol + // + this.anglecontrol.BackColor = System.Drawing.SystemColors.Control; + this.anglecontrol.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D; + this.anglecontrol.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.anglecontrol.Location = new System.Drawing.Point(156, 13); + this.anglecontrol.Name = "anglecontrol"; + this.anglecontrol.Size = new System.Drawing.Size(84, 84); + this.anglecontrol.TabIndex = 2; + this.anglecontrol.Value = 0; + this.anglecontrol.ButtonClicked += new System.EventHandler(this.anglecontrol_ButtonClicked); + // + // taglabel + // + taglabel.AutoSize = true; + taglabel.Location = new System.Drawing.Point(28, 31); + taglabel.Name = "taglabel"; + taglabel.Size = new System.Drawing.Size(27, 14); + taglabel.TabIndex = 6; + taglabel.Text = "Tag:"; + // + // cancel + // + this.cancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.cancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.cancel.Location = new System.Drawing.Point(558, 406); + this.cancel.Name = "cancel"; + this.cancel.Size = new System.Drawing.Size(112, 25); + this.cancel.TabIndex = 2; + this.cancel.Text = "Cancel"; + this.cancel.UseVisualStyleBackColor = true; + this.cancel.Click += new System.EventHandler(this.cancel_Click); + // + // apply + // + this.apply.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.apply.Location = new System.Drawing.Point(439, 406); + this.apply.Name = "apply"; + this.apply.Size = new System.Drawing.Size(112, 25); + this.apply.TabIndex = 1; + this.apply.Text = "OK"; + this.apply.UseVisualStyleBackColor = true; + this.apply.Click += new System.EventHandler(this.apply_Click); + // + // thingproperties + // + this.thingproperties.Controls.Add(this.groupBox3); + this.thingproperties.Controls.Add(this.spritetex); + this.thingproperties.Controls.Add(groupBox2); + this.thingproperties.Controls.Add(this.settingsgroup); + this.thingproperties.Controls.Add(groupBox1); + this.thingproperties.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.thingproperties.Location = new System.Drawing.Point(4, 2); + this.thingproperties.Name = "thingproperties"; + this.thingproperties.Padding = new System.Windows.Forms.Padding(3); + this.thingproperties.Size = new System.Drawing.Size(652, 398); + this.thingproperties.TabIndex = 0; + this.thingproperties.Text = "Properties"; + // + // groupBox3 + // + this.groupBox3.Controls.Add(this.tag); + this.groupBox3.Controls.Add(taglabel); + this.groupBox3.Controls.Add(this.newtag); + this.groupBox3.Location = new System.Drawing.Point(284, 215); + this.groupBox3.Name = "groupBox3"; + this.groupBox3.Size = new System.Drawing.Size(362, 66); + this.groupBox3.TabIndex = 23; + this.groupBox3.TabStop = false; + this.groupBox3.Text = " Identification "; + // + // tag + // + this.tag.AllowDecimal = false; + this.tag.AllowNegative = false; + this.tag.AllowRelative = true; + this.tag.ButtonStep = 1; + this.tag.Location = new System.Drawing.Point(62, 26); + this.tag.Name = "tag"; + this.tag.Size = new System.Drawing.Size(80, 24); + this.tag.StepValues = null; + this.tag.TabIndex = 7; + // + // newtag + // + this.newtag.Location = new System.Drawing.Point(154, 27); + this.newtag.Name = "newtag"; + this.newtag.Size = new System.Drawing.Size(76, 23); + this.newtag.TabIndex = 1; + this.newtag.Text = "New Tag"; + this.newtag.UseVisualStyleBackColor = true; + // + // spritetex + // + this.spritetex.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.spritetex.BackColor = System.Drawing.SystemColors.AppWorkspace; + this.spritetex.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Zoom; + this.spritetex.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D; + this.spritetex.Location = new System.Drawing.Point(284, 292); + this.spritetex.Name = "spritetex"; + this.spritetex.Size = new System.Drawing.Size(104, 100); + this.spritetex.TabIndex = 22; + // + // settingsgroup + // + this.settingsgroup.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.settingsgroup.Controls.Add(this.flags); + this.settingsgroup.Location = new System.Drawing.Point(284, 6); + this.settingsgroup.Name = "settingsgroup"; + this.settingsgroup.Size = new System.Drawing.Size(362, 208); + this.settingsgroup.TabIndex = 1; + this.settingsgroup.TabStop = false; + this.settingsgroup.Text = " Settings "; + // + // flags + // + this.flags.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.flags.AutoScroll = true; + this.flags.Columns = 2; + this.flags.Location = new System.Drawing.Point(18, 26); + this.flags.Name = "flags"; + this.flags.Size = new System.Drawing.Size(338, 175); + this.flags.TabIndex = 0; + // + // ThingEditForm + // + this.AcceptButton = this.apply; + this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; + this.CancelButton = this.cancel; + this.ClientSize = new System.Drawing.Size(680, 441); + this.Controls.Add(this.cancel); + this.Controls.Add(this.apply); + this.Controls.Add(this.thingproperties); + this.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "ThingEditForm"; + this.Opacity = 0D; + this.ShowIcon = false; + this.ShowInTaskbar = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Edit Thing"; + this.HelpRequested += new System.Windows.Forms.HelpEventHandler(this.ThingEditForm_HelpRequested); + groupBox1.ResumeLayout(false); + groupBox2.ResumeLayout(false); + groupBox2.PerformLayout(); + this.thingproperties.ResumeLayout(false); + this.groupBox3.ResumeLayout(false); + this.groupBox3.PerformLayout(); + this.settingsgroup.ResumeLayout(false); + this.ResumeLayout(false); + + } + + #endregion + private System.Windows.Forms.Button cancel; + private System.Windows.Forms.Button apply; + private System.Windows.Forms.Panel thingproperties; + private System.Windows.Forms.GroupBox groupBox3; + private Controls.ButtonsNumericTextbox tag; + private System.Windows.Forms.Button newtag; + private System.Windows.Forms.Panel spritetex; + private Controls.ButtonsNumericTextbox height; + private Controls.ButtonsNumericTextbox angle; + private System.Windows.Forms.Label heightlabel; + private Controls.AngleControl anglecontrol; + private System.Windows.Forms.GroupBox settingsgroup; + private Controls.CheckboxArrayControl flags; + private Controls.ThingBrowserControl thingtype; + } +} \ No newline at end of file diff --git a/Source/Core/Windows/ThingEditForm.cs b/Source/Core/Windows/ThingEditForm.cs new file mode 100644 index 0000000..865826a --- /dev/null +++ b/Source/Core/Windows/ThingEditForm.cs @@ -0,0 +1,279 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Text; +using System.Windows.Forms; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Data; +using CodeImp.DoomBuilder.IO; +using System.IO; +using CodeImp.DoomBuilder.Config; +using CodeImp.DoomBuilder.Editing; +using CodeImp.DoomBuilder.Geometry; +using CodeImp.DoomBuilder.Controls; + +#endregion + +namespace CodeImp.DoomBuilder.Windows +{ + /// + /// Dialog window that allows viewing and editing of Thing properties. + /// + public partial class ThingEditForm : DelayedForm + { + #region ================== Variables + + private ICollection things; + private List nodes; + private ThingTypeInfo thinginfo; + + #endregion + + #region ================== Properties + + #endregion + + #region ================== Constructor + + // Constructor + public ThingEditForm() + { + // Initialize + InitializeComponent(); + + // Fill flags list + foreach (KeyValuePair tf in General.Map.Config.ThingFlags) + flags.Add(tf.Value, tf.Key); + + // villsa - hide thing tag if specified false + if (!General.Map.FormatInterface.HasThingTag) + groupBox3.Hide(); + + // Thing height? + height.Visible = General.Map.FormatInterface.HasThingHeight; + heightlabel.Visible = General.Map.FormatInterface.HasThingHeight; + + // Setup types list + thingtype.Setup(); + } + + // This sets up the form to edit the given things + public void Setup(ICollection things) + { + Thing ft; + + // Keep this list + this.things = things; + if (things.Count > 1) this.Text = "Edit Things (" + things.Count + ")"; + + //////////////////////////////////////////////////////////////////////// + // Set all options to the first thing properties + //////////////////////////////////////////////////////////////////////// + + ft = General.GetByIndex(things, 0); + + // Set type + thingtype.SelectType(ft.Type); + + // Flags + foreach (CheckBox c in flags.Checkboxes) + if (ft.Flags.ContainsKey(c.Tag.ToString())) c.Checked = ft.Flags[c.Tag.ToString()]; + + // Coordination + angle.Text = Angle2D.RealToDoom(ft.Angle).ToString(); + height.Text = ((int)ft.Position.z).ToString(); + + // Tag + tag.Text = ft.Tag.ToString(); + + //////////////////////////////////////////////////////////////////////// + // Now go for all lines and change the options when a setting is different + //////////////////////////////////////////////////////////////////////// + + // Go for all things + foreach (Thing t in things) + { + // Type does not match? + if ((thingtype.GetSelectedInfo() != null) && + (thingtype.GetSelectedInfo().Index != t.Type)) + thingtype.ClearSelectedType(); + + // Flags + foreach (CheckBox c in flags.Checkboxes) + { + if (t.Flags.ContainsKey(c.Tag.ToString())) + { + if (t.Flags[c.Tag.ToString()] != c.Checked) + { + c.ThreeState = true; + c.CheckState = CheckState.Indeterminate; + } + } + } + + // Coordination + int angledeg = Angle2D.RealToDoom(t.Angle); + if (angledeg.ToString() != angle.Text) angle.Text = ""; + if (((int)t.Position.z).ToString() != height.Text) height.Text = ""; + + // Tag + if (t.Tag.ToString() != tag.Text) tag.Text = ""; + } + } + + #endregion + + #region ================== Interface + + // This finds a new (unused) tag + private void newtag_Click(object sender, EventArgs e) + { + tag.Text = General.Map.Map.GetNewTag().ToString(); + } + + // Selected type changes + private void thingtype_OnTypeChanged(ThingTypeInfo value) + { + thinginfo = value; + + // Update preview image + if (thinginfo != null) + { + if (thinginfo.Title == "Camera") // villsa 9/11/11 + { + General.DisplayZoomedImage(spritetex, General.Map.Data.ThingCamera.GetBitmap()); + } + else if (thinginfo.Title == "Trigger") // villsa 9/11/11 + { + General.DisplayZoomedImage(spritetex, General.Map.Data.ThingTrigger.GetBitmap()); + } + else if (thinginfo.Sprite.ToLowerInvariant().StartsWith(DataManager.INTERNAL_PREFIX) && + (thinginfo.Sprite.Length > DataManager.INTERNAL_PREFIX.Length)) + { + General.DisplayZoomedImage(spritetex, General.Map.Data.GetSpriteImage(thinginfo.Sprite).GetBitmap()); + } + else if ((thinginfo.Sprite.Length <= 8) && (thinginfo.Sprite.Length > 0)) + { + General.DisplayZoomedImage(spritetex, General.Map.Data.GetSpriteImage(thinginfo.Sprite).GetPreview()); + } + else + { + spritetex.BackgroundImage = null; + } + } + else + { + spritetex.BackgroundImage = null; + } + } + + // Angle text changes + private void angle_TextChanged(object sender, EventArgs e) + { + anglecontrol.Value = angle.GetResult(int.MinValue); + } + + // Angle control clicked + private void anglecontrol_ButtonClicked(object sender, EventArgs e) + { + angle.Text = anglecontrol.Value.ToString(); + } + + // Apply clicked + private void apply_Click(object sender, EventArgs e) + { + List defaultflags = new List(); + string undodesc = "thing"; + + // Verify the tag + if (General.Map.FormatInterface.HasThingTag && ((tag.GetResult(0) < General.Map.FormatInterface.MinTag) || (tag.GetResult(0) > General.Map.FormatInterface.MaxTag))) + { + General.ShowWarningMessage("Thing tag must be between " + General.Map.FormatInterface.MinTag + " and " + General.Map.FormatInterface.MaxTag + ".", MessageBoxButtons.OK); + return; + } + + // Verify the type + if (((thingtype.GetResult(0) < General.Map.FormatInterface.MinThingType) || (thingtype.GetResult(0) > General.Map.FormatInterface.MaxThingType))) + { + General.ShowWarningMessage("Thing type must be between " + General.Map.FormatInterface.MinThingType + " and " + General.Map.FormatInterface.MaxThingType + ".", MessageBoxButtons.OK); + return; + } + + // Make undo + if (things.Count > 1) undodesc = things.Count + " things"; + General.Map.UndoRedo.CreateUndo("Edit " + undodesc); + + // Go for all the things + foreach (Thing t in things) + { + // Thing type index + t.Type = General.Clamp(thingtype.GetResult(t.Type), General.Map.FormatInterface.MinThingType, General.Map.FormatInterface.MaxThingType); + + // Coordination + t.Rotate(Angle2D.DoomToReal(angle.GetResult(Angle2D.RealToDoom(t.Angle)))); + t.Move(t.Position.x, t.Position.y, (float)height.GetResult((int)t.Position.z)); + + // Apply all flags + foreach (CheckBox c in flags.Checkboxes) + { + if (c.CheckState == CheckState.Checked) t.SetFlag(c.Tag.ToString(), true); + else if (c.CheckState == CheckState.Unchecked) t.SetFlag(c.Tag.ToString(), false); + } + + // Tag + t.Tag = tag.GetResult(t.Tag); + + // Update settings + t.UpdateConfiguration(); + } + + // Set as defaults + foreach (CheckBox c in flags.Checkboxes) + if (c.CheckState == CheckState.Checked) defaultflags.Add(c.Tag.ToString()); + General.Settings.DefaultThingType = thingtype.GetResult(General.Settings.DefaultThingType); + General.Settings.DefaultThingAngle = Angle2D.DegToRad((float)angle.GetResult((int)Angle2D.RadToDeg(General.Settings.DefaultThingAngle) - 90) + 90); + General.Settings.SetDefaultThingFlags(defaultflags); + + // Done + General.Map.IsChanged = true; + this.DialogResult = DialogResult.OK; + this.Close(); + } + + // Cancel clicked + private void cancel_Click(object sender, EventArgs e) + { + // Be gone + this.DialogResult = DialogResult.Cancel; + this.Close(); + } + + // Help + private void ThingEditForm_HelpRequested(object sender, HelpEventArgs hlpevent) + { + General.ShowHelp("w_thingeditor.html"); + hlpevent.Handled = true; + } + + #endregion + } +} diff --git a/Source/Core/Windows/ThingEditForm.resx b/Source/Core/Windows/ThingEditForm.resx new file mode 100644 index 0000000..89976c1 --- /dev/null +++ b/Source/Core/Windows/ThingEditForm.resx @@ -0,0 +1,177 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + False + + + True + + + False + + + True + + + True + + + True + + + True + + + False + + + True + + + True + + + False + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + \ No newline at end of file diff --git a/Source/Core/Windows/ThingsFiltersForm.Designer.cs b/Source/Core/Windows/ThingsFiltersForm.Designer.cs new file mode 100644 index 0000000..41bcc8f --- /dev/null +++ b/Source/Core/Windows/ThingsFiltersForm.Designer.cs @@ -0,0 +1,237 @@ +namespace CodeImp.DoomBuilder.Windows +{ + partial class ThingsFiltersForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.listfilters = new System.Windows.Forms.ListView(); + this.columnname = new System.Windows.Forms.ColumnHeader(); + this.addfilter = new System.Windows.Forms.Button(); + this.deletefilter = new System.Windows.Forms.Button(); + this.filtergroup = new System.Windows.Forms.GroupBox(); + this.filterfields = new CodeImp.DoomBuilder.Controls.CheckboxArrayControl(); + this.label3 = new System.Windows.Forms.Label(); + this.filtercategory = new System.Windows.Forms.ComboBox(); + this.label2 = new System.Windows.Forms.Label(); + this.filtername = new System.Windows.Forms.TextBox(); + this.label1 = new System.Windows.Forms.Label(); + this.cancel = new System.Windows.Forms.Button(); + this.apply = new System.Windows.Forms.Button(); + this.filtergroup.SuspendLayout(); + this.SuspendLayout(); + // + // listfilters + // + this.listfilters.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left))); + this.listfilters.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.columnname}); + this.listfilters.FullRowSelect = true; + this.listfilters.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.None; + this.listfilters.HideSelection = false; + this.listfilters.Location = new System.Drawing.Point(12, 12); + this.listfilters.MultiSelect = false; + this.listfilters.Name = "listfilters"; + this.listfilters.ShowGroups = false; + this.listfilters.Size = new System.Drawing.Size(202, 323); + this.listfilters.Sorting = System.Windows.Forms.SortOrder.Ascending; + this.listfilters.TabIndex = 0; + this.listfilters.UseCompatibleStateImageBehavior = false; + this.listfilters.View = System.Windows.Forms.View.Details; + this.listfilters.SelectedIndexChanged += new System.EventHandler(this.listfilters_SelectedIndexChanged); + // + // columnname + // + this.columnname.Text = "Configuration"; + this.columnname.Width = 177; + // + // addfilter + // + this.addfilter.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.addfilter.Location = new System.Drawing.Point(12, 341); + this.addfilter.Name = "addfilter"; + this.addfilter.Size = new System.Drawing.Size(98, 25); + this.addfilter.TabIndex = 1; + this.addfilter.Text = "New Filter"; + this.addfilter.UseVisualStyleBackColor = true; + this.addfilter.Click += new System.EventHandler(this.addfilter_Click); + // + // deletefilter + // + this.deletefilter.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.deletefilter.Enabled = false; + this.deletefilter.Location = new System.Drawing.Point(116, 341); + this.deletefilter.Name = "deletefilter"; + this.deletefilter.Size = new System.Drawing.Size(98, 25); + this.deletefilter.TabIndex = 2; + this.deletefilter.Text = "Delete Selected"; + this.deletefilter.UseVisualStyleBackColor = true; + this.deletefilter.Click += new System.EventHandler(this.deletefilter_Click); + // + // filtergroup + // + this.filtergroup.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.filtergroup.Controls.Add(this.filterfields); + this.filtergroup.Controls.Add(this.label3); + this.filtergroup.Controls.Add(this.filtercategory); + this.filtergroup.Controls.Add(this.label2); + this.filtergroup.Controls.Add(this.filtername); + this.filtergroup.Controls.Add(this.label1); + this.filtergroup.Enabled = false; + this.filtergroup.Location = new System.Drawing.Point(232, 12); + this.filtergroup.Name = "filtergroup"; + this.filtergroup.Size = new System.Drawing.Size(382, 354); + this.filtergroup.TabIndex = 3; + this.filtergroup.TabStop = false; + this.filtergroup.Text = " Filter settings "; + // + // filterfields + // + this.filterfields.AutoScroll = true; + this.filterfields.Columns = 2; + this.filterfields.Location = new System.Drawing.Point(18, 125); + this.filterfields.Name = "filterfields"; + this.filterfields.Size = new System.Drawing.Size(329, 198); + this.filterfields.TabIndex = 2; + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Location = new System.Drawing.Point(15, 106); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(89, 14); + this.label3.TabIndex = 4; + this.label3.Text = "Filter by settings:"; + // + // filtercategory + // + this.filtercategory.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.filtercategory.FormattingEnabled = true; + this.filtercategory.Location = new System.Drawing.Point(115, 66); + this.filtercategory.Name = "filtercategory"; + this.filtercategory.Size = new System.Drawing.Size(232, 22); + this.filtercategory.TabIndex = 1; + this.filtercategory.SelectedIndexChanged += new System.EventHandler(this.filtercategory_SelectedIndexChanged); + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(15, 69); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(94, 14); + this.label2.TabIndex = 2; + this.label2.Text = "Filter by category:"; + // + // filtername + // + this.filtername.Location = new System.Drawing.Point(115, 28); + this.filtername.MaxLength = 50; + this.filtername.Name = "filtername"; + this.filtername.Size = new System.Drawing.Size(232, 20); + this.filtername.TabIndex = 0; + this.filtername.Validating += new System.ComponentModel.CancelEventHandler(this.filtername_Validating); + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(72, 31); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(37, 14); + this.label1.TabIndex = 0; + this.label1.Text = "Name:"; + // + // cancel + // + this.cancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.cancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.cancel.Location = new System.Drawing.Point(502, 383); + this.cancel.Name = "cancel"; + this.cancel.Size = new System.Drawing.Size(112, 25); + this.cancel.TabIndex = 5; + this.cancel.Text = "Cancel"; + this.cancel.UseVisualStyleBackColor = true; + this.cancel.Click += new System.EventHandler(this.cancel_Click); + // + // apply + // + this.apply.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.apply.Location = new System.Drawing.Point(384, 383); + this.apply.Name = "apply"; + this.apply.Size = new System.Drawing.Size(112, 25); + this.apply.TabIndex = 4; + this.apply.Text = "OK"; + this.apply.UseVisualStyleBackColor = true; + this.apply.Click += new System.EventHandler(this.apply_Click); + // + // ThingsFiltersForm + // + this.AcceptButton = this.apply; + this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; + this.CancelButton = this.cancel; + this.ClientSize = new System.Drawing.Size(624, 418); + this.Controls.Add(this.cancel); + this.Controls.Add(this.apply); + this.Controls.Add(this.filtergroup); + this.Controls.Add(this.deletefilter); + this.Controls.Add(this.addfilter); + this.Controls.Add(this.listfilters); + this.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "ThingsFiltersForm"; + this.ShowIcon = false; + this.ShowInTaskbar = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Things Filters"; + this.HelpRequested += new System.Windows.Forms.HelpEventHandler(this.ThingsFiltersForm_HelpRequested); + this.filtergroup.ResumeLayout(false); + this.filtergroup.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.ListView listfilters; + private System.Windows.Forms.ColumnHeader columnname; + private System.Windows.Forms.Button addfilter; + private System.Windows.Forms.Button deletefilter; + private System.Windows.Forms.GroupBox filtergroup; + private System.Windows.Forms.Button cancel; + private System.Windows.Forms.Button apply; + private System.Windows.Forms.TextBox filtername; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.ComboBox filtercategory; + private System.Windows.Forms.Label label3; + private CodeImp.DoomBuilder.Controls.CheckboxArrayControl filterfields; + } +} \ No newline at end of file diff --git a/Source/Core/Windows/ThingsFiltersForm.cs b/Source/Core/Windows/ThingsFiltersForm.cs new file mode 100644 index 0000000..9a4cbf5 --- /dev/null +++ b/Source/Core/Windows/ThingsFiltersForm.cs @@ -0,0 +1,296 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Text; +using System.Windows.Forms; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Data; +using CodeImp.DoomBuilder.IO; +using System.IO; +using CodeImp.DoomBuilder.Config; +using CodeImp.DoomBuilder.Editing; +using CodeImp.DoomBuilder.Geometry; +using CodeImp.DoomBuilder.Controls; + +#endregion + +namespace CodeImp.DoomBuilder.Windows +{ + internal partial class ThingsFiltersForm : DelayedForm + { + #region ================== Variables + + private bool settingup; + + #endregion + + #region ================== Constructor + + // Constructor + public ThingsFiltersForm() + { + settingup = true; + + // Initialize + InitializeComponent(); + + // Fill the categories combobox + filtercategory.Items.Add("(any category)"); + filtercategory.Items.AddRange(General.Map.Data.ThingCategories.ToArray()); + + // Fill checkboxes list + foreach (KeyValuePair flag in General.Map.Config.ThingFlags) + { + CheckBox box = filterfields.Add(flag.Value, flag.Key); + box.ThreeState = true; + box.CheckStateChanged += new EventHandler(filterfield_Check); + } + + // Fill list of filters + foreach (ThingsFilter f in General.Map.ConfigSettings.ThingsFilters) + { + // Make a copy (we don't want to modify the filters until OK is clicked) + ThingsFilter nf = new ThingsFilter(f); + + // Make item in list + ListViewItem item = new ListViewItem(nf.Name); + item.Tag = nf; + listfilters.Items.Add(item); + + // Select item if this is the current filter + if (General.Map.ThingsFilter == f) item.Selected = true; + } + + // Sort the list + listfilters.Sort(); + + // Done + settingup = false; + } + + #endregion + + #region ================== Management + + // OK clicked + private void apply_Click(object sender, EventArgs e) + { + // Clear all filters and add the new ones + General.Map.ConfigSettings.ThingsFilters.Clear(); + foreach (ListViewItem item in listfilters.Items) + General.Map.ConfigSettings.ThingsFilters.Add(item.Tag as ThingsFilter); + + // Update stuff + General.Map.ChangeThingFilter(new NullThingsFilter()); + General.MainWindow.UpdateThingsFilters(); + + // Close + this.DialogResult = DialogResult.OK; + this.Close(); + } + + // Cancel clicked + private void cancel_Click(object sender, EventArgs e) + { + // Close + this.DialogResult = DialogResult.Cancel; + this.Close(); + } + + // New Filter clicked + private void addfilter_Click(object sender, EventArgs e) + { + ThingsFilter newf = new ThingsFilter(); + + // Make item in list and select it + ListViewItem item = new ListViewItem(newf.Name); + item.Tag = newf; + listfilters.Items.Add(item); + item.Selected = true; + + // Focus on the name field + filtername.Focus(); + filtername.SelectAll(); + } + + // Delete Selected clicked + private void deletefilter_Click(object sender, EventArgs e) + { + // Anything selected? + if (listfilters.SelectedItems.Count > 0) + { + // Remove item + listfilters.Items.Remove(listfilters.SelectedItems[0]); + } + } + + // Item selected + private void listfilters_SelectedIndexChanged(object sender, EventArgs e) + { + // Anything selected? + if (listfilters.SelectedItems.Count > 0) + { + // Get selected filter + ThingsFilter f = listfilters.SelectedItems[0].Tag as ThingsFilter; + + // Enable settings + settingup = true; + deletefilter.Enabled = true; + filtergroup.Enabled = true; + + // Show name + filtername.Text = f.Name; + + // Show category + foreach (object c in filtercategory.Items) + { + ThingCategory tc = (c as ThingCategory); + if ((tc != null) && (tc.Name == f.CategoryName)) filtercategory.SelectedItem = tc; + } + if (filtercategory.SelectedIndex == -1) filtercategory.SelectedIndex = 0; + + // Show fields + foreach (CheckBox b in filterfields.Checkboxes) + { + // Field name forbidden? + if (f.ForbiddenFields.Contains(b.Tag.ToString())) + { + b.CheckState = CheckState.Unchecked; + } + // Field name required? + else if (f.RequiredFields.Contains(b.Tag.ToString())) + { + b.CheckState = CheckState.Checked; + } + else + { + b.CheckState = CheckState.Indeterminate; + } + } + + // Done + settingup = false; + } + else + { + // Disable filter settings + deletefilter.Enabled = false; + filtergroup.Enabled = false; + filtername.Text = ""; + filtercategory.SelectedIndex = -1; + foreach (CheckBox c in filterfields.Checkboxes) c.CheckState = CheckState.Indeterminate; + } + } + + // Help + private void ThingsFiltersForm_HelpRequested(object sender, HelpEventArgs hlpevent) + { + General.ShowHelp("w_thingsfilter.html"); + hlpevent.Handled = true; + } + + #endregion + + #region ================== Filter Settings + + // Category changed + private void filtercategory_SelectedIndexChanged(object sender, EventArgs e) + { + // Anything selected? + if (listfilters.SelectedItems.Count > 0) + { + // Get selected filter + ThingsFilter f = listfilters.SelectedItems[0].Tag as ThingsFilter; + + // Category selected + if ((filtercategory.SelectedIndex > -1) && (filtercategory.SelectedItem is ThingCategory)) + { + // Set new category name + f.CategoryName = (filtercategory.SelectedItem as ThingCategory).Name; + } + else + { + // Unset category name + f.CategoryName = ""; + } + } + } + + // Rename filter + private void filtername_Validating(object sender, CancelEventArgs e) + { + // Anything selected? + if (listfilters.SelectedItems.Count > 0) + { + // Get selected filter + ThingsFilter f = listfilters.SelectedItems[0].Tag as ThingsFilter; + + // Name changed? + if (f.Name != filtername.Text) + { + // Update name + f.Name = filtername.Text; + listfilters.SelectedItems[0].Text = f.Name; + listfilters.Sort(); + } + } + } + + // Field clicked + private void filterfield_Check(object sender, EventArgs e) + { + // Get the checkbox + CheckBox box = (sender as CheckBox); + + // Not setting up? + if (!settingup) + { + // Anything selected? + if (listfilters.SelectedItems.Count > 0) + { + // Get selected filter + ThingsFilter f = listfilters.SelectedItems[0].Tag as ThingsFilter; + + // New state is required? + if (box.CheckState == CheckState.Checked) + { + f.ForbiddenFields.Remove(box.Tag.ToString()); + if (!f.RequiredFields.Contains(box.Tag.ToString())) f.RequiredFields.Add(box.Tag.ToString()); + } + // New state is forbidden? + else if (box.CheckState == CheckState.Unchecked) + { + f.RequiredFields.Remove(box.Tag.ToString()); + if (!f.ForbiddenFields.Contains(box.Tag.ToString())) f.ForbiddenFields.Add(box.Tag.ToString()); + } + else + { + f.ForbiddenFields.Remove(box.Tag.ToString()); + f.RequiredFields.Remove(box.Tag.ToString()); + } + } + } + } + + #endregion + } +} \ No newline at end of file diff --git a/Source/Core/Windows/ThingsFiltersForm.resx b/Source/Core/Windows/ThingsFiltersForm.resx new file mode 100644 index 0000000..ec5e1fc --- /dev/null +++ b/Source/Core/Windows/ThingsFiltersForm.resx @@ -0,0 +1,159 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + \ No newline at end of file diff --git a/Source/Core/Windows/VertexEditForm.Designer.cs b/Source/Core/Windows/VertexEditForm.Designer.cs new file mode 100644 index 0000000..21d2199 --- /dev/null +++ b/Source/Core/Windows/VertexEditForm.Designer.cs @@ -0,0 +1,222 @@ +namespace CodeImp.DoomBuilder.Windows +{ + partial class VertexEditForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.Windows.Forms.TabPage tabproperties; + System.Windows.Forms.Label label1; + System.Windows.Forms.Label label6; + this.groupposition = new System.Windows.Forms.GroupBox(); + this.tabs = new System.Windows.Forms.TabControl(); + this.tabcustom = new System.Windows.Forms.TabPage(); + this.fieldslist = new CodeImp.DoomBuilder.Controls.FieldsEditorControl(); + this.cancel = new System.Windows.Forms.Button(); + this.apply = new System.Windows.Forms.Button(); + this.positionx = new CodeImp.DoomBuilder.Controls.ButtonsNumericTextbox(); + this.positiony = new CodeImp.DoomBuilder.Controls.ButtonsNumericTextbox(); + tabproperties = new System.Windows.Forms.TabPage(); + label1 = new System.Windows.Forms.Label(); + label6 = new System.Windows.Forms.Label(); + tabproperties.SuspendLayout(); + this.groupposition.SuspendLayout(); + this.tabs.SuspendLayout(); + this.tabcustom.SuspendLayout(); + this.SuspendLayout(); + // + // tabproperties + // + tabproperties.Controls.Add(this.groupposition); + tabproperties.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + tabproperties.Location = new System.Drawing.Point(4, 23); + tabproperties.Name = "tabproperties"; + tabproperties.Padding = new System.Windows.Forms.Padding(3); + tabproperties.Size = new System.Drawing.Size(428, 206); + tabproperties.TabIndex = 0; + tabproperties.Text = "Properties"; + tabproperties.UseVisualStyleBackColor = true; + // + // groupposition + // + this.groupposition.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.groupposition.Controls.Add(this.positiony); + this.groupposition.Controls.Add(this.positionx); + this.groupposition.Controls.Add(label1); + this.groupposition.Controls.Add(label6); + this.groupposition.Location = new System.Drawing.Point(7, 6); + this.groupposition.Name = "groupposition"; + this.groupposition.Size = new System.Drawing.Size(415, 194); + this.groupposition.TabIndex = 0; + this.groupposition.TabStop = false; + this.groupposition.Text = " Position "; + // + // label1 + // + label1.AutoSize = true; + label1.Location = new System.Drawing.Point(212, 39); + label1.Name = "label1"; + label1.Size = new System.Drawing.Size(18, 14); + label1.TabIndex = 23; + label1.Text = "Y:"; + // + // label6 + // + label6.AutoSize = true; + label6.Location = new System.Drawing.Point(45, 39); + label6.Name = "label6"; + label6.Size = new System.Drawing.Size(17, 14); + label6.TabIndex = 21; + label6.Text = "X:"; + // + // tabs + // + this.tabs.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.tabs.Controls.Add(tabproperties); + this.tabs.Controls.Add(this.tabcustom); + this.tabs.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tabs.Location = new System.Drawing.Point(10, 10); + this.tabs.Margin = new System.Windows.Forms.Padding(1); + this.tabs.Name = "tabs"; + this.tabs.SelectedIndex = 0; + this.tabs.Size = new System.Drawing.Size(436, 233); + this.tabs.SizeMode = System.Windows.Forms.TabSizeMode.Fixed; + this.tabs.TabIndex = 0; + // + // tabcustom + // + this.tabcustom.Controls.Add(this.fieldslist); + this.tabcustom.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tabcustom.Location = new System.Drawing.Point(4, 23); + this.tabcustom.Name = "tabcustom"; + this.tabcustom.Padding = new System.Windows.Forms.Padding(3); + this.tabcustom.Size = new System.Drawing.Size(428, 206); + this.tabcustom.TabIndex = 1; + this.tabcustom.Text = "Custom"; + this.tabcustom.UseVisualStyleBackColor = true; + // + // fieldslist + // + this.fieldslist.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.fieldslist.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D; + this.fieldslist.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.fieldslist.Location = new System.Drawing.Point(11, 11); + this.fieldslist.Margin = new System.Windows.Forms.Padding(8); + this.fieldslist.Name = "fieldslist"; + this.fieldslist.Size = new System.Drawing.Size(406, 187); + this.fieldslist.TabIndex = 2; + // + // cancel + // + this.cancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.cancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.cancel.Location = new System.Drawing.Point(334, 259); + this.cancel.Name = "cancel"; + this.cancel.Size = new System.Drawing.Size(112, 25); + this.cancel.TabIndex = 2; + this.cancel.Text = "Cancel"; + this.cancel.UseVisualStyleBackColor = true; + this.cancel.Click += new System.EventHandler(this.cancel_Click); + // + // apply + // + this.apply.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.apply.Location = new System.Drawing.Point(215, 259); + this.apply.Name = "apply"; + this.apply.Size = new System.Drawing.Size(112, 25); + this.apply.TabIndex = 1; + this.apply.Text = "OK"; + this.apply.UseVisualStyleBackColor = true; + this.apply.Click += new System.EventHandler(this.apply_Click); + // + // positionx + // + this.positionx.AllowDecimal = false; + this.positionx.AllowNegative = true; + this.positionx.AllowRelative = true; + this.positionx.ButtonStep = 1; + this.positionx.Location = new System.Drawing.Point(68, 34); + this.positionx.Name = "positionx"; + this.positionx.Size = new System.Drawing.Size(120, 24); + this.positionx.TabIndex = 24; + // + // positiony + // + this.positiony.AllowDecimal = false; + this.positiony.AllowNegative = true; + this.positiony.AllowRelative = true; + this.positiony.ButtonStep = 1; + this.positiony.Location = new System.Drawing.Point(236, 34); + this.positiony.Name = "positiony"; + this.positiony.Size = new System.Drawing.Size(120, 24); + this.positiony.TabIndex = 25; + // + // VertexEditForm + // + this.AcceptButton = this.apply; + this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; + this.CancelButton = this.cancel; + this.ClientSize = new System.Drawing.Size(456, 294); + this.Controls.Add(this.cancel); + this.Controls.Add(this.apply); + this.Controls.Add(this.tabs); + this.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "VertexEditForm"; + this.Opacity = 0; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Edit Vertex"; + this.HelpRequested += new System.Windows.Forms.HelpEventHandler(this.VertexEditForm_HelpRequested); + tabproperties.ResumeLayout(false); + this.groupposition.ResumeLayout(false); + this.groupposition.PerformLayout(); + this.tabs.ResumeLayout(false); + this.tabcustom.ResumeLayout(false); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.TabControl tabs; + private System.Windows.Forms.TabPage tabcustom; + private System.Windows.Forms.Button cancel; + private System.Windows.Forms.Button apply; + private CodeImp.DoomBuilder.Controls.FieldsEditorControl fieldslist; + private System.Windows.Forms.GroupBox groupposition; + private CodeImp.DoomBuilder.Controls.ButtonsNumericTextbox positiony; + private CodeImp.DoomBuilder.Controls.ButtonsNumericTextbox positionx; + } +} \ No newline at end of file diff --git a/Source/Core/Windows/VertexEditForm.cs b/Source/Core/Windows/VertexEditForm.cs new file mode 100644 index 0000000..8d87ee2 --- /dev/null +++ b/Source/Core/Windows/VertexEditForm.cs @@ -0,0 +1,177 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Text; +using System.Windows.Forms; +using CodeImp.DoomBuilder.Geometry; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Data; +using CodeImp.DoomBuilder.IO; +using System.IO; +using CodeImp.DoomBuilder.Config; +using CodeImp.DoomBuilder.Editing; +using CodeImp.DoomBuilder.Controls; + +#endregion + +namespace CodeImp.DoomBuilder.Windows +{ + public partial class VertexEditForm : DelayedForm + { + #region ================== Constants + + #endregion + + #region ================== Variables + + private ICollection vertices; + + #endregion + + #region ================== Properties + + #endregion + + #region ================== Constructor + + // Constructor + public VertexEditForm() + { + InitializeComponent(); + + // Fill universal fields list + fieldslist.ListFixedFields(General.Map.Config.VertexFields); + + // Custom fields? + if (!General.Map.FormatInterface.HasCustomFields) + tabs.TabPages.Remove(tabcustom); + + // Decimals allowed? + if (General.Map.FormatInterface.VertexDecimals > 0) + { + positionx.AllowDecimal = true; + positiony.AllowDecimal = true; + } + + // Initialize custom fields editor + fieldslist.Setup("vertex"); + } + + #endregion + + #region ================== Methods + + // This sets up the form to edit the given vertices + public void Setup(ICollection vertices) + { + // Keep this list + this.vertices = vertices; + if (vertices.Count > 1) this.Text = "Edit Vertices (" + vertices.Count + ")"; + + //////////////////////////////////////////////////////////////////////// + // Set all options to the first vertex properties + //////////////////////////////////////////////////////////////////////// + + // Get first vertex + Vertex vc = General.GetByIndex(vertices, 0); + + // Position + positionx.Text = vc.Position.x.ToString(); + positiony.Text = vc.Position.y.ToString(); + + // Custom fields + fieldslist.SetValues(vc.Fields, true); + + //////////////////////////////////////////////////////////////////////// + // Now go for all sectors and change the options when a setting is different + //////////////////////////////////////////////////////////////////////// + + // Go for all vertices + foreach (Vertex v in vertices) + { + // Position + if (positionx.Text != v.Position.x.ToString()) positionx.Text = ""; + if (positiony.Text != v.Position.y.ToString()) positiony.Text = ""; + + // Custom fields + fieldslist.SetValues(v.Fields, false); + } + } + + #endregion + + #region ================== Events + + // OK clicked + private void apply_Click(object sender, EventArgs e) + { + string undodesc = "vertex"; + + // Verify the coordinates + if ((positionx.GetResultFloat(0.0f) < General.Map.FormatInterface.MinCoordinate) || (positionx.GetResultFloat(0.0f) > General.Map.FormatInterface.MaxCoordinate) || + (positiony.GetResultFloat(0.0f) < General.Map.FormatInterface.MinCoordinate) || (positiony.GetResultFloat(0.0f) > General.Map.FormatInterface.MaxCoordinate)) + { + General.ShowWarningMessage("Vertex coordinates must be between " + General.Map.FormatInterface.MinCoordinate + " and " + General.Map.FormatInterface.MaxCoordinate + ".", MessageBoxButtons.OK); + return; + } + + // Make undo + if (vertices.Count > 1) undodesc = vertices.Count + " vertices"; + General.Map.UndoRedo.CreateUndo("Edit " + undodesc); + + // Go for all vertices + foreach (Vertex v in vertices) + { + // Apply position + Vector2D p = new Vector2D(); + p.x = General.Clamp(positionx.GetResultFloat(v.Position.x), (float)General.Map.FormatInterface.MinCoordinate, (float)General.Map.FormatInterface.MaxCoordinate); + p.y = General.Clamp(positiony.GetResultFloat(v.Position.y), (float)General.Map.FormatInterface.MinCoordinate, (float)General.Map.FormatInterface.MaxCoordinate); + v.Move(p); + + // Custom fields + fieldslist.Apply(v.Fields); + } + + // Done + General.Map.IsChanged = true; + this.DialogResult = DialogResult.OK; + this.Close(); + } + + // Cancel clicked + private void cancel_Click(object sender, EventArgs e) + { + // Just close + this.DialogResult = DialogResult.Cancel; + this.Close(); + } + + // Help requested + private void VertexEditForm_HelpRequested(object sender, HelpEventArgs hlpevent) + { + General.ShowHelp("w_vertexeditor.html"); + hlpevent.Handled = true; + } + + #endregion + } +} \ No newline at end of file diff --git a/Source/Core/Windows/VertexEditForm.resx b/Source/Core/Windows/VertexEditForm.resx new file mode 100644 index 0000000..ae7ecd1 --- /dev/null +++ b/Source/Core/Windows/VertexEditForm.resx @@ -0,0 +1,165 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + True + + + False + + + True + + + True + + + True + + + True + + + False + + + True + + + False + + + True + + + True + + + True + + + True + + + True + + + True + + \ No newline at end of file diff --git a/Source/Core/ZDoom/ActorStructure.cs b/Source/Core/ZDoom/ActorStructure.cs new file mode 100644 index 0000000..24bb707 --- /dev/null +++ b/Source/Core/ZDoom/ActorStructure.cs @@ -0,0 +1,549 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Data; +using System.IO; +using System.Diagnostics; +using CodeImp.DoomBuilder.Compilers; + +#endregion + +namespace CodeImp.DoomBuilder.ZDoom +{ + public sealed class ActorStructure + { + #region ================== Constants + + private readonly string[] SPRITE_POSTFIXES = new string[] { "2C8", "2D8", "2A8", "2B8", "1C1", "1D1", "1A1", "1B1", "A2", "A1", "A0", "2", "1", "0" }; + + #endregion + + #region ================== Variables + + // Declaration + private string classname; + private string inheritclass; + private string replaceclass; + private int doomednum = -1; + + // Flags + private Dictionary flags; + + // Properties + private Dictionary> props; + + // States + private Dictionary states; + + #endregion + + #region ================== Properties + + public Dictionary Flags { get { return flags; } } + public Dictionary> Properties { get { return props; } } + public string ClassName { get { return classname; } } + public string InheritsClass { get { return inheritclass; } } + public string ReplacesClass { get { return replaceclass; } } + public int DoomEdNum { get { return doomednum; } } + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + internal ActorStructure(DecorateParser parser) + { + // Initialize + flags = new Dictionary(); + props = new Dictionary>(); + states = new Dictionary(); + + // Always define a game property, but default to 0 values + props["game"] = new List(); + + inheritclass = "actor"; + replaceclass = null; + + // First next token is the class name + parser.SkipWhitespace(true); + classname = parser.StripTokenQuotes(parser.ReadToken()); + if (string.IsNullOrEmpty(classname)) + { + parser.ReportError("Expected actor class name"); + return; + } + + // Parse tokens before entering the actor scope + while (parser.SkipWhitespace(true)) + { + string token = parser.ReadToken(); + if (!string.IsNullOrEmpty(token)) + { + token = token.ToLowerInvariant(); + if (token == ":") + { + // The next token must be the class to inherit from + parser.SkipWhitespace(true); + inheritclass = parser.StripTokenQuotes(parser.ReadToken()); + if (string.IsNullOrEmpty(inheritclass) || parser.IsSpecialToken(inheritclass)) + { + parser.ReportError("Expected class name to inherit from"); + return; + } + else + { + // Find the actor to inherit from + ActorStructure other = parser.GetArchivedActorByName(inheritclass); + if (other != null) + InheritFrom(other); + else + General.ErrorLogger.Add(ErrorType.Warning, "Unable to find the DECORATE class '" + inheritclass + "' to inherit from, while parsing '" + classname + "'"); + } + } + else if (token == "replaces") + { + // The next token must be the class to replace + parser.SkipWhitespace(true); + replaceclass = parser.StripTokenQuotes(parser.ReadToken()); + if (string.IsNullOrEmpty(replaceclass) || parser.IsSpecialToken(replaceclass)) + { + parser.ReportError("Expected class name to replace"); + return; + } + } + else if (token == "native") + { + // Igore this token + } + else if (token == "{") + { + // Actor scope begins here, + // break out of this parse loop + break; + } + else if (token == "-") + { + // This could be a negative doomednum (but our parser sees the - as separate token) + // So read whatever is after this token and ignore it (negative doomednum indicates no doomednum) + parser.ReadToken(); + } + else + { + // Check if numeric + if (!int.TryParse(token, NumberStyles.Integer, CultureInfo.InvariantCulture, out doomednum)) + { + // Not numeric! + parser.ReportError("Expected numeric editor thing number or start of actor scope"); + return; + } + } + } + else + { + parser.ReportError("Unexpected end of structure"); + return; + } + } + + // Now parse the contents of actor structure + string previoustoken = ""; + while (parser.SkipWhitespace(true)) + { + string token = parser.ReadToken(); + token = token.ToLowerInvariant(); + + if ((token == "+") || (token == "-")) + { + // Next token is a flag (option) to set or remove + bool flagvalue = (token == "+"); + parser.SkipWhitespace(true); + string flagname = parser.ReadToken(); + if (!string.IsNullOrEmpty(flagname)) + { + // Add the flag with its value + flagname = flagname.ToLowerInvariant(); + flags[flagname] = flagvalue; + } + else + { + parser.ReportError("Expected flag name"); + return; + } + } + else if ((token == "action") || (token == "native")) + { + // We don't need this, ignore up to the first next ; + while (parser.SkipWhitespace(true)) + { + string t = parser.ReadToken(); + if ((t == ";") || (t == null)) break; + } + } + else if (token == "states") + { + // Now parse actor states until we reach the end of the states structure + while (parser.SkipWhitespace(true)) + { + string statetoken = parser.ReadToken(); + if (!string.IsNullOrEmpty(statetoken)) + { + // Start of scope? + if (statetoken == "{") + { + // This is fine + } + // End of scope? + else if (statetoken == "}") + { + // Done with the states, + // break out of this parse loop + break; + } + // State label? + else if (statetoken == ":") + { + if (!string.IsNullOrEmpty(previoustoken)) + { + // Parse actor state + StateStructure st = new StateStructure(parser, previoustoken); + if (parser.HasError) return; + states[previoustoken.ToLowerInvariant()] = st; + } + else + { + parser.ReportError("Unexpected end of structure"); + return; + } + } + else + { + // Keep token + previoustoken = statetoken; + } + } + else + { + parser.ReportError("Unexpected end of structure"); + return; + } + } + } + else if (token == "}") + { + // Actor scope ends here, + // break out of this parse loop + break; + } + // Monster property? + else if (token == "monster") + { + // This sets certain flags we are interested in + flags["shootable"] = true; + flags["countkill"] = true; + flags["solid"] = true; + flags["canpushwalls"] = true; + flags["canusewalls"] = true; + flags["activatemcross"] = true; + flags["canpass"] = true; + flags["ismonster"] = true; + } + // Projectile property? + else if (token == "projectile") + { + // This sets certain flags we are interested in + flags["noblockmap"] = true; + flags["nogravity"] = true; + flags["dropoff"] = true; + flags["missile"] = true; + flags["activateimpact"] = true; + flags["activatepcross"] = true; + flags["noteleport"] = true; + } + // Clearflags property? + else if (token == "clearflags") + { + // Clear all flags + flags.Clear(); + } + // Game property? + else if (token == "game") + { + // Include all tokens on the same line + List games = new List(); + while (parser.SkipWhitespace(false)) + { + string v = parser.ReadToken(); + if (v == null) + { + parser.ReportError("Unexpected end of structure"); + return; + } + if (v == "\n") break; + if (v != ",") + games.Add(v.ToLowerInvariant()); + } + props[token] = games; + } + // Property + else + { + // Property begins with $? Then the whole line is a single value + if (token.StartsWith("$")) + { + // This is for editor-only properties such as $sprite and $category + List values = new List(); + if (parser.SkipWhitespace(false)) + values.Add(parser.ReadLine()); + else + values.Add(""); + props[token] = values; + } + else + { + // Next tokens up until the next newline are values + List values = new List(); + while (parser.SkipWhitespace(false)) + { + string v = parser.ReadToken(); + if (v == null) + { + parser.ReportError("Unexpected end of structure"); + return; + } + if (v == "\n") break; + if (v != ",") + values.Add(v); + } + props[token] = values; + } + } + + // Keep token + previoustoken = token; + } + } + + #endregion + + #region ================== Methods + + // This is called to inherit properties from another actor + private void InheritFrom(ActorStructure baseactor) + { + this.flags = new Dictionary(baseactor.flags); + this.props = new Dictionary>(baseactor.props.Count); + this.states = new Dictionary(baseactor.states); + + // Copy props + foreach (KeyValuePair> p in baseactor.props) + this.props.Add(p.Key, new List(p.Value)); + + // Reset the game property, because it is never inherited + props["game"] = new List(); + } + + /// + /// This checks if the actor has a specific property. + /// + public bool HasProperty(string propname) + { + return props.ContainsKey(propname); + } + + /// + /// This checks if the actor has a specific property with at least one value. + /// + public bool HasPropertyWithValue(string propname) + { + return (props.ContainsKey(propname) && (props[propname].Count > 0)); + } + + /// + /// This returns values of a specific property as a complete string. Returns an empty string when the propery has no values. + /// + public string GetPropertyAllValues(string propname) + { + return HasPropertyWithValue(propname) ? string.Join(" ", props[propname].ToArray()) : ""; + } + + /// + /// This returns a specific value of a specific property as a string. Returns an empty string when the propery does not have the specified value. + /// + public string GetPropertyValueString(string propname, int valueindex) + { + if (HasProperty(propname) && (props[propname].Count > valueindex)) + return props[propname][valueindex]; + else + return ""; + } + + /// + /// This returns a specific value of a specific property as an integer. Returns 0 when the propery does not have the specified value. + /// + public int GetPropertyValueInt(string propname, int valueindex) + { + if (HasProperty(propname) && (props[propname].Count > valueindex)) + { + int intvalue; + if (int.TryParse(props[propname][valueindex], NumberStyles.Integer, CultureInfo.InvariantCulture, out intvalue)) + return intvalue; + else + return 0; + } + else + { + return 0; + } + } + + /// + /// This returns a specific value of a specific property as a float. Returns 0.0f when the propery does not have the specified value. + /// + public float GetPropertyValueFloat(string propname, int valueindex) + { + if (HasProperty(propname) && (props[propname].Count > valueindex)) + { + float fvalue; + if (float.TryParse(props[propname][valueindex], NumberStyles.Float, CultureInfo.InvariantCulture, out fvalue)) + return fvalue; + else + return 0.0f; + } + else + { + return 0.0f; + } + } + + /// + /// This returns the status of a flag. + /// + public bool HasFlagValue(string flag) + { + return flags.ContainsKey(flag); + } + + /// + /// This returns the status of a flag. + /// + public bool GetFlagValue(string flag, bool defaultvalue) + { + if (flags.ContainsKey(flag)) + return flags[flag]; + else + return defaultvalue; + } + + /// + /// This checks if this actor is meant for the current decorate game support + /// + public bool CheckActorSupported() + { + // Check if we want to include this actor + string includegames = General.Map.Config.DecorateGames.ToLowerInvariant(); + bool includeactor = (props["game"].Count == 0); + foreach (string g in props["game"]) + includeactor |= includegames.Contains(g); + + return includeactor; + } + + /// + /// This finds the best suitable sprite to use when presenting this actor to the user. + /// + public string FindSuitableSprite() + { + string result = ""; + + // Sprite forced? + if (props.ContainsKey("$sprite")) + { + return props["$sprite"][0]; + } + else + { + // Try the idle state + if (states.ContainsKey("idle")) + { + StateStructure s = states["idle"]; + if (!string.IsNullOrEmpty(s.FirstSprite)) + result = s.FirstSprite; + } + + // Try the see state + if (string.IsNullOrEmpty(result) && states.ContainsKey("see")) + { + StateStructure s = states["see"]; + if (!string.IsNullOrEmpty(s.FirstSprite)) + result = s.FirstSprite; + } + + // Try the inactive state + if (string.IsNullOrEmpty(result) && states.ContainsKey("inactive")) + { + StateStructure s = states["inactive"]; + if (!string.IsNullOrEmpty(s.FirstSprite)) + result = s.FirstSprite; + } + + // Try the spawn state + if (string.IsNullOrEmpty(result) && states.ContainsKey("spawn")) + { + StateStructure s = states["spawn"]; + if (!string.IsNullOrEmpty(s.FirstSprite)) + result = s.FirstSprite; + } + + // Still no sprite found? then just pick the first we can find + if (string.IsNullOrEmpty(result)) + { + foreach (StateStructure s in states.Values) + { + if (!string.IsNullOrEmpty(s.FirstSprite)) + { + result = s.FirstSprite; + break; + } + } + } + + if (!string.IsNullOrEmpty(result)) + { + // The sprite name is not actually complete, we still have to append + // the direction characters to it. Find an existing sprite with direction. + foreach (string postfix in SPRITE_POSTFIXES) + { + if (General.Map.Data.GetSpriteExists(result + postfix)) + return result + postfix; + } + } + } + + // No sprite found + return ""; + } + + #endregion + } +} diff --git a/Source/Core/ZDoom/DecorateParser.cs b/Source/Core/ZDoom/DecorateParser.cs new file mode 100644 index 0000000..bb86159 --- /dev/null +++ b/Source/Core/ZDoom/DecorateParser.cs @@ -0,0 +1,243 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Data; +using System.IO; +using System.Diagnostics; +using CodeImp.DoomBuilder.Compilers; + +#endregion + +namespace CodeImp.DoomBuilder.ZDoom +{ + public sealed class DecorateParser : ZDTextParser + { + #region ================== Delegates + + public delegate void IncludeDelegate(DecorateParser parser, string includefile); + + public IncludeDelegate OnInclude; + + #endregion + + #region ================== Constants + + #endregion + + #region ================== Variables + + // These are actors we want to keep + private Dictionary actors; + + // These are all parsed actors, also those from other games + private Dictionary archivedactors; + + #endregion + + #region ================== Properties + + /// + /// All actors that are supported by the current game. + /// + public ICollection Actors { get { return actors.Values; } } + + /// + /// All actors defined in the loaded DECORATE structures. This includes actors not supported in the current game. + /// + public ICollection AllActors { get { return archivedactors.Values; } } + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + public DecorateParser() + { + // Syntax + whitespace = "\n \t\r"; + specialtokens = ":{}+-\n;,"; + + // Initialize + actors = new Dictionary(); + archivedactors = new Dictionary(); + } + + #endregion + + #region ================== Parsing + + // This parses the given decorate stream + // Returns false on errors + public override bool Parse(Stream stream, string sourcefilename) + { + base.Parse(stream, sourcefilename); + + // Keep local data + Stream localstream = datastream; + string localsourcename = sourcename; + BinaryReader localreader = datareader; + + // Continue until at the end of the stream + while (SkipWhitespace(true)) + { + // Read a token + string objdeclaration = ReadToken(); + if (objdeclaration != null) + { + objdeclaration = objdeclaration.ToLowerInvariant(); + if (objdeclaration == "actor") + { + // Read actor structure + ActorStructure actor = new ActorStructure(this); + if (this.HasError) break; + + // Add the actor + archivedactors[actor.ClassName.ToLowerInvariant()] = actor; + if (actor.CheckActorSupported()) + actors[actor.ClassName.ToLowerInvariant()] = actor; + + // Replace an actor? + if (actor.ReplacesClass != null) + { + if (GetArchivedActorByName(actor.ReplacesClass) != null) + archivedactors[actor.ReplacesClass.ToLowerInvariant()] = actor; + else + General.ErrorLogger.Add(ErrorType.Warning, "Unable to find the DECORATE class '" + actor.ReplacesClass + "' to replace, while parsing '" + actor.ClassName + "'"); + + if (actor.CheckActorSupported()) + { + if (GetActorByName(actor.ReplacesClass) != null) + actors[actor.ReplacesClass.ToLowerInvariant()] = actor; + } + } + } + else if (objdeclaration == "#include") + { + // Include a file + SkipWhitespace(true); + string filename = ReadToken(); + if (!string.IsNullOrEmpty(filename)) + { + // Strip the quotes + filename = filename.Replace("\"", ""); + + // Callback to parse this file now + if (OnInclude != null) OnInclude(this, filename); + + // Set our buffers back to continue parsing + datastream = localstream; + datareader = localreader; + sourcename = localsourcename; + if (HasError) break; + } + else + { + ReportError("Expected file name to include"); + break; + } + } + else if ((objdeclaration == "const") || (objdeclaration == "native")) + { + // We don't need this, ignore up to the first next ; + while (SkipWhitespace(true)) + { + string t = ReadToken(); + if ((t == ";") || (t == null)) break; + } + } + else + { + // Unknown structure! + // Best we can do now is just find the first { and then + // follow the scopes until the matching } is found + string token2; + do + { + if (!SkipWhitespace(true)) break; + token2 = ReadToken(); + if (token2 == null) break; + } + while (token2 != "{"); + int scopelevel = 1; + do + { + if (!SkipWhitespace(true)) break; + token2 = ReadToken(); + if (token2 == null) break; + if (token2 == "{") scopelevel++; + if (token2 == "}") scopelevel--; + } + while (scopelevel > 0); + } + } + } + + // Return true when no errors occurred + return (ErrorDescription == null); + } + + #endregion + + #region ================== Methods + + /// + /// This returns a supported actor by name. Returns null when no supported actor with the specified name can be found. This operation is of O(1) complexity. + /// + public ActorStructure GetActorByName(string name) + { + name = name.ToLowerInvariant(); + if (actors.ContainsKey(name)) + return actors[name]; + else + return null; + } + + /// + /// This returns a supported actor by DoomEdNum. Returns null when no supported actor with the specified name can be found. Please note that this operation is of O(n) complexity! + /// + public ActorStructure GetActorByDoomEdNum(int doomednum) + { + ICollection collection = actors.Values; + foreach (ActorStructure a in collection) + { + if (a.DoomEdNum == doomednum) + return a; + } + return null; + } + + // This returns an actor by name + // Returns null when actor cannot be found + internal ActorStructure GetArchivedActorByName(string name) + { + name = name.ToLowerInvariant(); + if (archivedactors.ContainsKey(name)) + return archivedactors[name]; + else + return null; + } + + #endregion + } +} diff --git a/Source/Core/ZDoom/PatchStructure.cs b/Source/Core/ZDoom/PatchStructure.cs new file mode 100644 index 0000000..22d9e8d --- /dev/null +++ b/Source/Core/ZDoom/PatchStructure.cs @@ -0,0 +1,223 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Data; +using System.IO; +using System.Diagnostics; +using CodeImp.DoomBuilder.Compilers; + +#endregion + +namespace CodeImp.DoomBuilder.ZDoom +{ + public sealed class PatchStructure + { + #region ================== Constants + + #endregion + + #region ================== Variables + + // Declaration + private string name; + private int offsetx; + private int offsety; + private bool flipx; + private bool flipy; + private float alpha; + + #endregion + + #region ================== Properties + + public string Name { get { return name; } } + public int OffsetX { get { return offsetx; } } + public int OffsetY { get { return offsety; } } + public bool FlipX { get { return flipx; } } + public bool FlipY { get { return flipy; } } + public float Alpha { get { return alpha; } } + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + internal PatchStructure(TexturesParser parser) + { + string tokenstr; + + // Initialize + alpha = 1.0f; + + // There should be 3 tokens separated by 2 commas now: + // Name, Width, Height + + // First token is the class name + parser.SkipWhitespace(true); + name = parser.StripTokenQuotes(parser.ReadToken()); + if (string.IsNullOrEmpty(name)) + { + parser.ReportError("Expected patch name"); + return; + } + + // Now we should find a comma + parser.SkipWhitespace(true); + tokenstr = parser.ReadToken(); + if (tokenstr != ",") + { + parser.ReportError("Expected a comma"); + return; + } + + // Next is the patch width + parser.SkipWhitespace(true); + tokenstr = parser.ReadToken(); + if (string.IsNullOrEmpty(tokenstr) || !int.TryParse(tokenstr, NumberStyles.Integer, CultureInfo.InvariantCulture, out offsetx)) + { + parser.ReportError("Expected offset in pixels"); + return; + } + + // Now we should find a comma again + parser.SkipWhitespace(true); + tokenstr = parser.ReadToken(); + if (tokenstr != ",") + { + parser.ReportError("Expected a comma"); + return; + } + + // Next is the patch height + parser.SkipWhitespace(true); + tokenstr = parser.ReadToken(); + if (string.IsNullOrEmpty(tokenstr) || !int.TryParse(tokenstr, NumberStyles.Integer, CultureInfo.InvariantCulture, out offsety)) + { + parser.ReportError("Expected offset in pixels"); + return; + } + + // Next token is the beginning of the texture scope. + // If not, then the patch info ends here. + parser.SkipWhitespace(true); + tokenstr = parser.ReadToken(); + if (tokenstr != "{") + { + // Rewind so this structure can be read again + parser.DataStream.Seek(-tokenstr.Length - 1, SeekOrigin.Current); + return; + } + + // Now parse the contents of texture structure + while (parser.SkipWhitespace(true)) + { + string token = parser.ReadToken(); + token = token.ToLowerInvariant(); + if (token == "flipx") + { + flipx = true; + } + else if (token == "flipy") + { + flipy = true; + } + else if (token == "alpha") + { + if (!ReadTokenFloat(parser, token, out alpha)) return; + alpha = General.Clamp(alpha, 0.0f, 1.0f); + } + else if (token == "}") + { + // Patch scope ends here, + // break out of this parse loop + break; + } + } + } + + #endregion + + #region ================== Methods + + // This reads the next token and sets a floating point value, returns false when failed + private bool ReadTokenFloat(TexturesParser parser, string propertyname, out float value) + { + // Next token is the property value to set + parser.SkipWhitespace(true); + string strvalue = parser.ReadToken(); + if (!string.IsNullOrEmpty(strvalue)) + { + // Try parsing as value + if (!float.TryParse(strvalue, NumberStyles.Float, CultureInfo.InvariantCulture, out value)) + { + parser.ReportError("Expected numeric value for property '" + propertyname + "'"); + return false; + } + else + { + // Success + return true; + } + } + else + { + // Can't find the property value! + parser.ReportError("Expected a value for property '" + propertyname + "'"); + value = 0.0f; + return false; + } + } + + // This reads the next token and sets an integral value, returns false when failed + private bool ReadTokenInt(TexturesParser parser, string propertyname, out int value) + { + // Next token is the property value to set + parser.SkipWhitespace(true); + string strvalue = parser.ReadToken(); + if (!string.IsNullOrEmpty(strvalue)) + { + // Try parsing as value + if (!int.TryParse(strvalue, NumberStyles.Integer, CultureInfo.InvariantCulture, out value)) + { + parser.ReportError("Expected integral value for property '" + propertyname + "'"); + return false; + } + else + { + // Success + return true; + } + } + else + { + // Can't find the property value! + parser.ReportError("Expected a value for property '" + propertyname + "'"); + value = 0; + return false; + } + } + + #endregion + } +} diff --git a/Source/Core/ZDoom/StateStructure.cs b/Source/Core/ZDoom/StateStructure.cs new file mode 100644 index 0000000..3e4da4c --- /dev/null +++ b/Source/Core/ZDoom/StateStructure.cs @@ -0,0 +1,152 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Data; +using System.IO; +using System.Diagnostics; +using CodeImp.DoomBuilder.Compilers; + +#endregion + +namespace CodeImp.DoomBuilder.ZDoom +{ + internal sealed class StateStructure + { + #region ================== Constants + + // Some odd thing in ZDoom + private const string IGNORE_SPRITE = "TNT1A0"; + + #endregion + + #region ================== Variables + + // All we care about is the first sprite in the sequence + private string firstsprite; + + #endregion + + #region ================== Properties + + public string FirstSprite { get { return firstsprite; } } + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + internal StateStructure(DecorateParser parser, string statename) + { + string lasttoken = ""; + firstsprite = null; + + // Skip whitespace + while (parser.SkipWhitespace(true)) + { + // Read first token + string token = parser.ReadToken(); + token = token.ToLowerInvariant(); + + // One of the flow control statements? + if ((token == "loop") || (token == "stop") || (token == "wait") || (token == "fail")) + { + // Ignore flow control + } + // Label? + else if (token == ":") + { + // Rewind so that this label can be read again + parser.DataStream.Seek(-(lasttoken.Length + 1), SeekOrigin.Current); + + // Done here + return; + } + // End of scope? + else if (token == "}") + { + // Rewind so that this scope end can be read again + parser.DataStream.Seek(-1, SeekOrigin.Current); + + // Done here + return; + } + else + { + // First part of the sprite name + if (token == null) + { + parser.ReportError("Unexpected end of structure"); + return; + } + + // Frames of the sprite name + parser.SkipWhitespace(true); + string spriteframes = parser.ReadToken(); + if (spriteframes == null) + { + parser.ReportError("Unexpected end of structure"); + return; + } + // Label? + else if (spriteframes == ":") + { + // Rewind so that this label can be read again + parser.DataStream.Seek(-(token.Length + 1), SeekOrigin.Current); + + // Done here + return; + } + + // No first sprite yet? + if ((firstsprite == null) && (spriteframes.Length > 0)) + { + // Make the sprite name + firstsprite = token + spriteframes[0]; + firstsprite = firstsprite.ToUpperInvariant(); + + // Ignore some odd ZDoom thing + if (IGNORE_SPRITE.StartsWith(firstsprite)) + firstsprite = null; + } + + // Continue until the end of the line + string t = ""; + while ((t != "\n") && (t != null)) + { + parser.SkipWhitespace(false); + t = parser.ReadToken(); + } + } + + lasttoken = token; + } + } + + #endregion + + #region ================== Methods + + #endregion + } +} diff --git a/Source/Core/ZDoom/TextureStructure.cs b/Source/Core/ZDoom/TextureStructure.cs new file mode 100644 index 0000000..a273014 --- /dev/null +++ b/Source/Core/ZDoom/TextureStructure.cs @@ -0,0 +1,283 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Data; +using System.IO; +using System.Diagnostics; +using CodeImp.DoomBuilder.Compilers; +using CodeImp.DoomBuilder.Rendering; + +#endregion + +namespace CodeImp.DoomBuilder.ZDoom +{ + public sealed class TextureStructure + { + #region ================== Constants + + #endregion + + #region ================== Variables + + // Declaration + private string name; + private int width; + private int height; + + // Properties + private float xscale; + private float yscale; + private int xoffset; + private int yoffset; + private bool worldpanning; + + // Patches + private List patches; + + #endregion + + #region ================== Properties + + public string Name { get { return name; } } + public int Width { get { return width; } } + public int Height { get { return height; } } + public float XScale { get { return xscale; } } + public float YScale { get { return yscale; } } + public int XOffset { get { return xoffset; } } + public int YOffset { get { return yoffset; } } + public bool WorldPanning { get { return worldpanning; } } + public ICollection Patches { get { return patches; } } + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + internal TextureStructure(TexturesParser parser) + { + string tokenstr; + + // Initialize + patches = new List(4); + xscale = 0.0f; + yscale = 0.0f; + + // There should be 3 tokens separated by 2 commas now: + // Name, Width, Height + + // First token is the class name + parser.SkipWhitespace(true); + name = parser.StripTokenQuotes(parser.ReadToken()); + if (string.IsNullOrEmpty(name)) + { + parser.ReportError("Expected texture or sprite name"); + return; + } + + // Now we should find a comma + parser.SkipWhitespace(true); + tokenstr = parser.ReadToken(); + if (tokenstr != ",") + { + parser.ReportError("Expected a comma"); + return; + } + + // Next is the texture width + parser.SkipWhitespace(true); + tokenstr = parser.ReadToken(); + if (string.IsNullOrEmpty(tokenstr) || !int.TryParse(tokenstr, NumberStyles.Integer, CultureInfo.InvariantCulture, out width)) + { + parser.ReportError("Expected width in pixels"); + return; + } + + // Now we should find a comma again + parser.SkipWhitespace(true); + tokenstr = parser.ReadToken(); + if (tokenstr != ",") + { + parser.ReportError("Expected a comma"); + return; + } + + // Next is the texture height + parser.SkipWhitespace(true); + tokenstr = parser.ReadToken(); + if (string.IsNullOrEmpty(tokenstr) || !int.TryParse(tokenstr, NumberStyles.Integer, CultureInfo.InvariantCulture, out height)) + { + parser.ReportError("Expected height in pixels"); + return; + } + + // Next token should be the beginning of the texture scope + parser.SkipWhitespace(true); + tokenstr = parser.ReadToken(); + if (tokenstr != "{") + { + parser.ReportError("Expected begin of structure"); + return; + } + + // Now parse the contents of texture structure + while (parser.SkipWhitespace(true)) + { + string token = parser.ReadToken(); + token = token.ToLowerInvariant(); + if (token == "xscale") + { + if (!ReadTokenFloat(parser, token, out xscale)) return; + } + else if (token == "yscale") + { + if (!ReadTokenFloat(parser, token, out yscale)) return; + } + else if (token == "worldpanning") + { + worldpanning = true; + } + else if (token == "offset") + { + // Read x offset + if (!ReadTokenInt(parser, token, out xoffset)) return; + + // Now we should find a comma + parser.SkipWhitespace(true); + tokenstr = parser.ReadToken(); + if (tokenstr != ",") + { + parser.ReportError("Expected a comma"); + return; + } + + // Read y offset + if (!ReadTokenInt(parser, token, out yoffset)) return; + } + else if (token == "patch") + { + // Read patch structure + PatchStructure pt = new PatchStructure(parser); + if (parser.HasError) break; + + // Add the patch + patches.Add(pt); + } + else if (token == "}") + { + // Actor scope ends here, + // break out of this parse loop + break; + } + } + } + + #endregion + + #region ================== Methods + + // This reads the next token and sets a floating point value, returns false when failed + private bool ReadTokenFloat(TexturesParser parser, string propertyname, out float value) + { + // Next token is the property value to set + parser.SkipWhitespace(true); + string strvalue = parser.ReadToken(); + if (!string.IsNullOrEmpty(strvalue)) + { + // Try parsing as value + if (!float.TryParse(strvalue, NumberStyles.Float, CultureInfo.InvariantCulture, out value)) + { + parser.ReportError("Expected numeric value for property '" + propertyname + "'"); + return false; + } + else + { + // Success + return true; + } + } + else + { + // Can't find the property value! + parser.ReportError("Expected a value for property '" + propertyname + "'"); + value = 0.0f; + return false; + } + } + + // This reads the next token and sets an integral value, returns false when failed + private bool ReadTokenInt(TexturesParser parser, string propertyname, out int value) + { + // Next token is the property value to set + parser.SkipWhitespace(true); + string strvalue = parser.ReadToken(); + if (!string.IsNullOrEmpty(strvalue)) + { + // Try parsing as value + if (!int.TryParse(strvalue, NumberStyles.Integer, CultureInfo.InvariantCulture, out value)) + { + parser.ReportError("Expected integral value for property '" + propertyname + "'"); + return false; + } + else + { + // Success + return true; + } + } + else + { + // Can't find the property value! + parser.ReportError("Expected a value for property '" + propertyname + "'"); + value = 0; + return false; + } + } + + // This makes a HighResImage texture for this texture + internal HighResImage MakeImage(Dictionary textures, Dictionary flats) + { + float scalex, scaley; + + // Determine default scale + float defaultscale = General.Map.Config.DefaultTextureScale; + + // Determine scale for texture + if (xscale == 0.0f) scalex = defaultscale; else scalex = 1f / xscale; + if (yscale == 0.0f) scaley = defaultscale; else scaley = 1f / yscale; + + // Make texture + HighResImage tex = new HighResImage(name, width, height, scalex, scaley, worldpanning); + + // Add patches + foreach (PatchStructure p in patches) + { + tex.AddPatch(new TexturePatch(p.Name.ToUpperInvariant(), p.OffsetX, p.OffsetY, p.FlipX, p.FlipY, 0, new PixelColor(0, 0, 0, 0), p.Alpha, 0)); + } + + return tex; + } + + #endregion + } +} diff --git a/Source/Core/ZDoom/TexturesParser.cs b/Source/Core/ZDoom/TexturesParser.cs new file mode 100644 index 0000000..6ca188a --- /dev/null +++ b/Source/Core/ZDoom/TexturesParser.cs @@ -0,0 +1,157 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Data; +using System.IO; +using System.Diagnostics; +using CodeImp.DoomBuilder.Compilers; + +#endregion + +namespace CodeImp.DoomBuilder.ZDoom +{ + public sealed class TexturesParser : ZDTextParser + { + #region ================== Delegates + + #endregion + + #region ================== Constants + + #endregion + + #region ================== Variables + + private List textures; + private List sprites; + + #endregion + + #region ================== Properties + + public ICollection Textures { get { return textures; } } + public ICollection Sprites { get { return sprites; } } + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + public TexturesParser() + { + // Syntax + whitespace = "\n \t\r"; + specialtokens = ",{}\n"; + + // Initialize + textures = new List(); + sprites = new List(); + } + + #endregion + + #region ================== Parsing + + // This parses the given stream + // Returns false on errors + public override bool Parse(Stream stream, string sourcefilename) + { + base.Parse(stream, sourcefilename); + + // Continue until at the end of the stream + while (SkipWhitespace(true)) + { + // Read a token + string objdeclaration = ReadToken(); + if (objdeclaration != null) + { + objdeclaration = objdeclaration.ToLowerInvariant(); + if (objdeclaration == "texture") + { + // Read texture structure + TextureStructure tx = new TextureStructure(this); + if (this.HasError) break; + + // if a limit for the texture name length is set make sure that it's not exceeded + if ((General.Map.Config.MaxTextureNamelength > 0) && (tx.Name.Length > General.Map.Config.MaxTextureNamelength)) + { + General.ErrorLogger.Add(ErrorType.Error, "Texture name \"" + tx.Name + "\" too long. Texture names must have a length of " + General.Map.Config.MaxTextureNamelength.ToString() + " characters or less"); + } + else + { + // Add the texture + textures.Add(tx); + } + } + else if (objdeclaration == "sprite") + { + // Read sprite structure + TextureStructure tx = new TextureStructure(this); + if (this.HasError) break; + + // if a limit for the sprite name length is set make sure that it's not exceeded + if ((General.Map.Config.MaxTextureNamelength > 0) && (tx.Name.Length > General.Map.Config.MaxTextureNamelength)) + { + General.ErrorLogger.Add(ErrorType.Error, "Sprite name \"" + tx.Name + "\" too long. Sprite names must have a length of " + General.Map.Config.MaxTextureNamelength.ToString() + " characters or less"); + } + else + { + // Add the sprite + sprites.Add(tx); + } + } + else + { + // Unknown structure! + // Best we can do now is just find the first { and then + // follow the scopes until the matching } is found + string token2; + do + { + if (!SkipWhitespace(true)) break; + token2 = ReadToken(); + if (token2 == null) break; + } + while (token2 != "{"); + int scopelevel = 1; + do + { + if (!SkipWhitespace(true)) break; + token2 = ReadToken(); + if (token2 == null) break; + if (token2 == "{") scopelevel++; + if (token2 == "}") scopelevel--; + } + while (scopelevel > 0); + } + } + } + + // Return true when no errors occurred + return (ErrorDescription == null); + } + + #endregion + } +} diff --git a/Source/Core/ZDoom/ZDTextParser.cs b/Source/Core/ZDoom/ZDTextParser.cs new file mode 100644 index 0000000..af3def3 --- /dev/null +++ b/Source/Core/ZDoom/ZDTextParser.cs @@ -0,0 +1,326 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Data; +using System.IO; +using System.Diagnostics; +using CodeImp.DoomBuilder.Compilers; + +#endregion + +namespace CodeImp.DoomBuilder.ZDoom +{ + public abstract class ZDTextParser + { + #region ================== Constants + + #endregion + + #region ================== Variables + + // Parsing + protected string whitespace = "\n \t\r"; + protected string specialtokens = ":{}+-\n;"; + + // Input data stream + protected Stream datastream; + protected BinaryReader datareader; + protected string sourcename; + + // Error report + private int errorline; + private string errordesc; + private string errorsource; + + #endregion + + #region ================== Properties + + internal Stream DataStream { get { return datastream; } } + internal BinaryReader DataReader { get { return datareader; } } + public int ErrorLine { get { return errorline; } } + public string ErrorDescription { get { return errordesc; } } + public string ErrorSource { get { return errorsource; } } + public bool HasError { get { return (errordesc != null); } } + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + protected ZDTextParser() + { + // Initialize + errordesc = null; + } + + #endregion + + #region ================== Parsing + + // This parses the given decorate stream + // Returns false on errors + public virtual bool Parse(Stream stream, string sourcefilename) + { + datastream = stream; + datareader = new BinaryReader(stream, Encoding.ASCII); + sourcename = sourcefilename; + datastream.Seek(0, SeekOrigin.Begin); + return true; + } + + // This returns true if the given character is whitespace + protected internal bool IsWhitespace(char c) + { + return (whitespace.IndexOf(c) > -1); + } + + // This returns true if the given character is a special token + protected internal bool IsSpecialToken(char c) + { + return (specialtokens.IndexOf(c) > -1); + } + + // This returns true if the given character is a special token + protected internal bool IsSpecialToken(string s) + { + if (s.Length > 0) + return (specialtokens.IndexOf(s[0]) > -1); + else + return false; + } + + // This removes beginning and ending quotes from a token + protected internal string StripTokenQuotes(string token) + { + // Remove first character, if it is a quote + if (!string.IsNullOrEmpty(token) && (token[0] == '"')) + token = token.Substring(1); + + // Remove last character, if it is a quote + if (!string.IsNullOrEmpty(token) && (token[token.Length - 1] == '"')) + token = token.Substring(0, token.Length - 1); + + return token; + } + + // This skips whitespace on the stream, placing the read + // position right before the first non-whitespace character + // Returns false when the end of the stream is reached + protected internal bool SkipWhitespace(bool skipnewline) + { + int offset = skipnewline ? 0 : 1; + char c; + + do + { + if (datastream.Position == datastream.Length) return false; + c = (char)datareader.ReadByte(); + + // Check if this is comment + if (c == '/') + { + if (datastream.Position == datastream.Length) return false; + char c2 = (char)datareader.ReadByte(); + if (c2 == '/') + { + // Check if not a special comment with a token + if (datastream.Position == datastream.Length) return false; + char c3 = (char)datareader.ReadByte(); + if (c3 != '$') + { + // Skip entire line + char c4 = ' '; + while ((c4 != '\n') && (datastream.Position < datastream.Length)) { c4 = (char)datareader.ReadByte(); } + c = c4; + } + else + { + // Not a comment + c = c3; + } + } + else if (c2 == '*') + { + // Skip until */ + char c4, c3 = '\0'; + do + { + c4 = c3; + c3 = (char)datareader.ReadByte(); + } + while ((c4 != '*') || (c3 != '/')); + c = ' '; + } + else + { + // Not a comment, rewind from reading c2 + datastream.Seek(-1, SeekOrigin.Current); + } + } + } + while (whitespace.IndexOf(c, offset) > -1); + + // Go one character back so we can read this non-whitespace character again + datastream.Seek(-1, SeekOrigin.Current); + return true; + } + + // This reads a token (all sequential non-whitespace characters or a single character) + // Returns null when the end of the stream has been reached + protected internal string ReadToken() + { + string token = ""; + bool quotedstring = false; + + // Return null when the end of the stream has been reached + if (datastream.Position == datastream.Length) return null; + + // Start reading + char c = (char)datareader.ReadByte(); + while (!IsWhitespace(c) || quotedstring || IsSpecialToken(c)) + { + // Special token? + if (!quotedstring && IsSpecialToken(c)) + { + // Not reading a token yet? + if (token.Length == 0) + { + // This is our whole token + token += c; + break; + } + else + { + // This is a new token and shouldn't be read now + // Go one character back so we can read this token again + datastream.Seek(-1, SeekOrigin.Current); + break; + } + } + else + { + // Quote? + if (c == '"') + { + // Quote to end the string? + if (quotedstring) quotedstring = false; + + // First character is a quote? + if (token.Length == 0) quotedstring = true; + + token += c; + } + // Potential comment? + else if (c == '/') + { + // Check the next byte + if (datastream.Position == datastream.Length) return token; + char c2 = (char)datareader.ReadByte(); + if ((c2 == '/') || (c2 == '*')) + { + // This is a comment start, so the token ends here + // Go two characters back so we can read this comment again + datastream.Seek(-2, SeekOrigin.Current); + break; + } + else + { + // Not a comment + // Go one character back so we can read this char again + datastream.Seek(-1, SeekOrigin.Current); + token += c; + } + } + else + { + token += c; + } + } + + // Next character + if (datastream.Position < datastream.Length) + c = (char)datareader.Read(); + else + break; + } + + return token; + } + + // This reads the rest of the line + // Returns null when the end of the stream has been reached + protected internal string ReadLine() + { + string token = ""; + + // Return null when the end of the stream has been reached + if (datastream.Position == datastream.Length) return null; + + // Start reading + char c = (char)datareader.ReadByte(); + while (c != '\n') + { + token += c; + + // Next character + if (datastream.Position < datastream.Length) + c = (char)datareader.Read(); + else + break; + } + + return token.Trim(); + } + + // This reports an error + protected internal void ReportError(string message) + { + long position = datastream.Position; + long readpos = 0; + int linenumber = 1; + + // Find the line on which we found this error + datastream.Seek(0, SeekOrigin.Begin); + StreamReader textreader = new StreamReader(datastream, Encoding.ASCII); + while (readpos < position) + { + string line = textreader.ReadLine(); + if (line == null) break; + readpos += line.Length + 2; + linenumber++; + } + + // Return to original position + datastream.Seek(position, SeekOrigin.Begin); + + // Set error information + errordesc = message; + errorline = linenumber; + errorsource = sourcename; + } + + #endregion + } +} diff --git a/Source/Plugins/BuilderModes/BuilderModes.csproj b/Source/Plugins/BuilderModes/BuilderModes.csproj new file mode 100644 index 0000000..37ce5ca --- /dev/null +++ b/Source/Plugins/BuilderModes/BuilderModes.csproj @@ -0,0 +1,309 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {B42D5AA0-F9A6-4234-9C4B-A05B11A64851} + Library + Properties + CodeImp.DoomBuilder.BuilderModes + BuilderModes + Always + + + 3.5 + + + v3.5 + + + true + ..\..\..\Build\Plugins\ + DEBUG;TRACE + true + full + x86 + prompt + + + ..\..\..\Build\Plugins\ + true + true + none + x86 + prompt + + + + + 3.5 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Form + + + CurveLinedefsForm.cs + + + Form + + + ErrorCheckForm.cs + + + Form + + + FindReplaceForm.cs + + + Form + + + MakeDoorForm.cs + + + Form + + + MenusForm.cs + + + Form + + + PreferencesForm.cs + + + True + True + Resources.resx + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Designer + CurveLinedefsForm.cs + + + Designer + MenusForm.cs + + + + + + {818B3D10-F791-4C3F-9AF5-BB2D0079B63C} + Builder + False + + + + + Designer + ErrorCheckForm.cs + + + FindReplaceForm.cs + Designer + + + Designer + MakeDoorForm.cs + + + Designer + ResXFileCodeGenerator + Resources.Designer.cs + + + + + + + + + + + + + + + + + + Designer + PreferencesForm.cs + + + + + + + + + + + + Designer + EditSelectionPanel.cs + + + Designer + UndoRedoPanel.cs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + UserControl + + + EditSelectionPanel.cs + + + UserControl + + + UndoRedoPanel.cs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Source/Plugins/BuilderModes/ClassicModes/BaseClassicMode.cs b/Source/Plugins/BuilderModes/ClassicModes/BaseClassicMode.cs new file mode 100644 index 0000000..2e69d5c --- /dev/null +++ b/Source/Plugins/BuilderModes/ClassicModes/BaseClassicMode.cs @@ -0,0 +1,209 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using CodeImp.DoomBuilder.Windows; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Geometry; +using CodeImp.DoomBuilder.Editing; +using CodeImp.DoomBuilder.Actions; +using CodeImp.DoomBuilder.Types; +using CodeImp.DoomBuilder.Config; + +#endregion + +namespace CodeImp.DoomBuilder.BuilderModes +{ + public abstract class BaseClassicMode : ClassicMode + { + #region ================== Constants + + #endregion + + #region ================== Variables + + #endregion + + #region ================== Properties + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + public BaseClassicMode() + { + // Initialize + + // We have no destructor + GC.SuppressFinalize(this); + } + + // Disposer + public override void Dispose() + { + // Not already disposed? + if (!isdisposed) + { + // Clean up + + // Done + base.Dispose(); + } + } + + #endregion + + #region ================== Methods + + // This occurs when the user presses Copy. All selected geometry must be marked for copying! + public override bool OnCopyBegin() + { + General.Map.Map.MarkAllSelectedGeometry(true, false, true, true, false); + + // Return true when anything is selected so that the copy continues + // We only have to check vertices for the geometry, because without selected + // vertices, no complete structure can exist. + return (General.Map.Map.GetMarkedVertices(true).Count > 0) || + (General.Map.Map.GetMarkedThings(true).Count > 0); + } + + // This is called when pasting begins + public override bool OnPasteBegin(PasteOptions options) + { + // These modes support pasting + return true; + } + + // This is called when something was pasted. + public override void OnPasteEnd(PasteOptions options) + { + General.Map.Map.ClearAllSelected(); + General.Map.Map.SelectMarkedGeometry(true, true); + + // Switch to EditSelectionMode + EditSelectionMode editmode = new EditSelectionMode(); + editmode.Pasting = true; + editmode.PasteOptions = options; + General.Editing.ChangeMode(editmode); + } + + // Double-clicking + public override void OnMouseDoubleClick(MouseEventArgs e) + { + base.OnMouseDoubleClick(e); + + int k = 0; + if (e.Button == MouseButtons.Left) k = (int)Keys.LButton; + if (e.Button == MouseButtons.Middle) k = (int)Keys.MButton; + if (e.Button == MouseButtons.Right) k = (int)Keys.RButton; + if (e.Button == MouseButtons.XButton1) k = (int)Keys.XButton1; + if (e.Button == MouseButtons.XButton2) k = (int)Keys.XButton2; + + // Double select-click? Make that the same as single edit-click + if (General.Actions.GetActionByName("builder_classicselect").KeyMatches(k)) + { + Actions.Action a = General.Actions.GetActionByName("builder_classicedit"); + if (a != null) a.Invoke(); + } + } + + #endregion + + #region ================== Actions + + [BeginAction("placevisualstart")] + public void PlaceVisualStartThing() + { + Thing thingfound = null; + + // Not during volatile mode + if (this.Attributes.Volatile) return; + + // Mouse must be inside window + if (!mouseinside) return; + + General.Interface.DisplayStatus(StatusType.Action, "Placed Visual Mode camera start thing."); + + // Go for all things + List things = new List(General.Map.Map.Things); + foreach (Thing t in things) + { + if (t.Type == General.Map.Config.Start3DModeThingType) + { + if (thingfound == null) + { + // Move this thing + t.Move(mousemappos); + thingfound = t; + } + else + { + // One was already found and moved, delete this one + t.Dispose(); + } + } + } + + // No thing found? + if (thingfound == null) + { + // Make a new one + Thing t = General.Map.Map.CreateThing(); + if (t != null) + { + t.Type = General.Map.Config.Start3DModeThingType; + t.Move(mousemappos); + t.UpdateConfiguration(); + General.Map.ThingsFilter.Update(); + thingfound = t; + } + } + + if (thingfound != null) + { + // Make sure that the found thing is between ceiling and floor + thingfound.DetermineSector(); + if (thingfound.Position.z < 0.0f) thingfound.Move(thingfound.Position.x, thingfound.Position.y, 0.0f); + if (thingfound.Sector != null) + { + if ((thingfound.Position.z + 50.0f) > (thingfound.Sector.CeilHeight - thingfound.Sector.FloorHeight)) + thingfound.Move(thingfound.Position.x, thingfound.Position.y, + thingfound.Sector.CeilHeight - thingfound.Sector.FloorHeight - 50.0f); + } + } + + // Update Visual Mode camera + General.Map.VisualCamera.PositionAtThing(); + + // Redraw display to show changes + General.Interface.RedrawDisplay(); + } + + #endregion + } +} diff --git a/Source/Plugins/BuilderModes/ClassicModes/BlockMapMode.cs b/Source/Plugins/BuilderModes/ClassicModes/BlockMapMode.cs new file mode 100644 index 0000000..b16586d --- /dev/null +++ b/Source/Plugins/BuilderModes/ClassicModes/BlockMapMode.cs @@ -0,0 +1,229 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using CodeImp.DoomBuilder.Windows; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Geometry; +using CodeImp.DoomBuilder.Editing; +using System.Drawing; +using CodeImp.DoomBuilder.Actions; +using CodeImp.DoomBuilder.Data; + +#endregion + +namespace CodeImp.DoomBuilder.BuilderModes +{ + [EditMode(DisplayName = "BlockMap Mode", + SwitchAction = "blockmapmode", + ButtonImage = "Blockmap.png", + ButtonOrder = 400, + ButtonGroup = "002_tools", + AllowCopyPaste = false, + UseByDefault = true, + SafeStartMode = true)] + + public sealed class BlockMapMode : BaseClassicMode + { + #region ================== Constants + + #endregion + + #region ================== Variables + + private BlockMap blockmap; + private BlockEntry highlighted; + private int hx, hy; + + #endregion + + #region ================== Properties + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + public BlockMapMode() + { + } + + // Disposer + public override void Dispose() + { + // Not already disposed? + if(!isdisposed) + { + // Dispose base + base.Dispose(); + } + } + + #endregion + + #region ================== Methods + + #endregion + + #region ================== Events + + // Mode engages + public override void OnEngage() + { + base.OnEngage(); + + // Nothing highlighted + highlighted = null; + hx = -1; + hy = -1; + + // Custom presentation to hide the grid + CustomPresentation p = new CustomPresentation(); + p.AddLayer(new PresentLayer(RendererLayer.Background, BlendingMode.Mask, General.Settings.BackgroundAlpha)); + p.AddLayer(new PresentLayer(RendererLayer.Surface, BlendingMode.Mask)); + p.AddLayer(new PresentLayer(RendererLayer.Overlay, BlendingMode.Alpha, 1f, true)); + p.AddLayer(new PresentLayer(RendererLayer.Things, BlendingMode.Alpha, 1.0f)); + p.AddLayer(new PresentLayer(RendererLayer.Geometry, BlendingMode.Alpha, 1f, true)); + renderer.SetPresentation(p); + + // Make the blockmap + RectangleF area = MapSet.CreateArea(General.Map.Map.Vertices); + area = MapSet.IncreaseArea(area, General.Map.Map.Things); + blockmap = new BlockMap(area); + blockmap.AddLinedefsSet(General.Map.Map.Linedefs); + blockmap.AddSectorsSet(General.Map.Map.Sectors); + blockmap.AddThingsSet(General.Map.Map.Things); + } + + // When disengaged + public override void OnDisengage() + { + base.OnDisengage(); + + } + + // This redraws the display + public override void OnRedrawDisplay() + { + renderer.RedrawSurface(); + + // Render lines and vertices + if(renderer.StartPlotter(true)) + { + renderer.PlotLinedefSet(General.Map.Map.Linedefs); + + if(highlighted != null) + { + foreach(Linedef l in highlighted.Lines) + { + renderer.PlotLinedef(l, General.Colors.Highlight); + } + } + + renderer.PlotVerticesSet(General.Map.Map.Vertices); + renderer.Finish(); + } + + // Render things + if(renderer.StartThings(true)) + { + renderer.RenderThingSet(General.Map.ThingsFilter.HiddenThings, Presentation.THINGS_HIDDEN_ALPHA); + renderer.RenderThingSet(General.Map.ThingsFilter.VisibleThings, 1.0f); + + if(highlighted != null) + { + foreach(Thing t in highlighted.Things) + { + renderer.RenderThing(t, General.Colors.Highlight, 1.0f); + } + } + + renderer.Finish(); + } + + // Render blocks + if(renderer.StartOverlay(true)) + { + for(int x = 0; x <= blockmap.Size.Width; x++) + { + Vector2D v1 = new Vector2D(blockmap.Range.X + (float)x * blockmap.BlockSize, blockmap.Range.Y); + Vector2D v2 = new Vector2D(v1.x, blockmap.Range.Y + blockmap.Size.Height * blockmap.BlockSize); + renderer.RenderLine(v1, v2, 2.0f, General.Colors.Selection, true); + } + + for(int y = 0; y <= blockmap.Size.Height; y++) + { + Vector2D v1 = new Vector2D(blockmap.Range.X, blockmap.Range.Y + (float)y * blockmap.BlockSize); + Vector2D v2 = new Vector2D(blockmap.Range.X + blockmap.Size.Width * blockmap.BlockSize, v1.y); + renderer.RenderLine(v1, v2, 2.0f, General.Colors.Selection, true); + } + + if((hx > -1) && (hy > -1)) + { + Vector2D v1 = new Vector2D(blockmap.Range.X + (float)hx * blockmap.BlockSize, blockmap.Range.Y + (float)hy * blockmap.BlockSize); + Vector2D v2 = new Vector2D(blockmap.Range.X + (float)(hx + 1) * blockmap.BlockSize, blockmap.Range.Y + (float)(hy + 1) * blockmap.BlockSize); + renderer.RenderRectangle(new RectangleF(v1.x, v1.y, v2.x - v1.x, v2.y - v1.y), 2.0f, General.Colors.Highlight, true); + } + + renderer.Finish(); + } + + renderer.Present(); + } + + // Mouse moves over display + public override void OnMouseMove(MouseEventArgs e) + { + base.OnMouseMove(e); + + BlockEntry newhighlight = null; + hx = -1; + hy = -1; + + // Determine highlighted block + float nhx = (mousemappos.x - blockmap.Range.Left) / (float)blockmap.BlockSize; + float nhy = (mousemappos.y - blockmap.Range.Top) / (float)blockmap.BlockSize; + if((nhx < (float)blockmap.Size.Width) && (nhy < (float)blockmap.Size.Height) && (nhx >= 0.0f) && (nhy >= 0.0f)) + { + newhighlight = blockmap.GetBlockAt(mousemappos); + if(newhighlight != null) + { + hx = (int)nhx; + hy = (int)nhy; + } + } + + if(newhighlight != highlighted) + { + highlighted = newhighlight; + General.Interface.RedrawDisplay(); + } + } + + #endregion + } +} diff --git a/Source/Plugins/BuilderModes/ClassicModes/CurveLinedefsMode.cs b/Source/Plugins/BuilderModes/ClassicModes/CurveLinedefsMode.cs new file mode 100644 index 0000000..317cc9c --- /dev/null +++ b/Source/Plugins/BuilderModes/ClassicModes/CurveLinedefsMode.cs @@ -0,0 +1,296 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using CodeImp.DoomBuilder.Windows; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Geometry; +using System.Drawing; +using CodeImp.DoomBuilder.Editing; + +#endregion + +namespace CodeImp.DoomBuilder.BuilderModes +{ + [EditMode(DisplayName = "Curve Linedefs", + Volatile = true)] + public sealed class CurveLinedefsMode : BaseClassicMode + { + #region ================== Constants + + private const float LINE_THICKNESS = 0.6f; + + #endregion + + #region ================== Variables + + // Collections + private ICollection selectedlines; + private ICollection unselectedlines; + + #endregion + + #region ================== Properties + + // Just keep the base mode button checked + public override string EditModeButtonName { get { return General.Editing.PreviousStableMode.Name; } } + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + public CurveLinedefsMode(EditMode basemode) + { + // Make collections by selection + selectedlines = General.Map.Map.GetSelectedLinedefs(true); + unselectedlines = General.Map.Map.GetSelectedLinedefs(false); + } + + // Disposer + public override void Dispose() + { + // Not already disposed? + if (!isdisposed) + { + // Clean up + + // Done + base.Dispose(); + } + } + + #endregion + + #region ================== Methods + + // This generates the vertices to split the line with, from start to end + private List GenerateCurve(Linedef line) + { + // Fetch settings from window + int vertices = BuilderPlug.Me.CurveLinedefsForm.Vertices; + float distance = BuilderPlug.Me.CurveLinedefsForm.Distance; + float angle = BuilderPlug.Me.CurveLinedefsForm.Angle; + bool fixedcurve = BuilderPlug.Me.CurveLinedefsForm.FixedCurve; + bool backwards = BuilderPlug.Me.CurveLinedefsForm.Backwards; + + // Make list + List points = new List(vertices); + + //Added by Anders �strand 2008-05-18 + //The formulas used are taken from http://mathworld.wolfram.com/CircularSegment.html + //c and theta are known (length of line and angle parameter). d, R and h are + //calculated from those two + //If the curve is not supposed to be a circular segment it's simply deformed to fit + //the value set for distance. + + //The vertices are generated to be evenly distributed (by angle) along the curve + //and lastly they are rotated and moved to fit with the original line + + //calculate some identities of a circle segment (refer to the graph in the url above) + double c = line.Length; + double theta = angle; + + double d = (c / Math.Tan(theta / 2)) / 2; + double R = d / Math.Cos(theta / 2); + double h = R - d; + + double yDeform = fixedcurve ? 1 : distance / h; + if (backwards) + yDeform = -yDeform; + + double a, x, y; + Vector2D vertex; + + for (int v = 1; v <= vertices; v++) + { + //calculate the angle for this vertex + //the curve starts at PI/2 - theta/2 and is segmented into vertices+1 segments + //this assumes the line is horisontal and on y = 0, the point is rotated and moved later + + a = (Math.PI - theta) / 2 + v * (theta / (vertices + 1)); + + //calculate the coordinates of the point, and distort the y coordinate + //using the deform factor calculated above + x = Math.Cos(a) * R; + y = (Math.Sin(a) * R - d) * yDeform; + + //rotate and transform to fit original line + vertex = new Vector2D((float)x, (float)y).GetRotated(line.Angle + Angle2D.PIHALF); + vertex = vertex.GetTransformed(line.GetCenterPoint().x, line.GetCenterPoint().y, 1, 1); + + points.Add(vertex); + } + + + // Done + return points; + } + + #endregion + + #region ================== Events + + public override void OnHelp() + { + General.ShowHelp("e_curvelinedefs.html"); + } + + // Cancelled + public override void OnCancel() + { + // Cancel base class + base.OnCancel(); + + // Return to base mode + General.Editing.ChangeMode(General.Editing.PreviousStableMode.Name); + } + + // Mode engages + public override void OnEngage() + { + base.OnEngage(); + renderer.SetPresentation(Presentation.Standard); + + // Show toolbox window + BuilderPlug.Me.CurveLinedefsForm.Show((Form)General.Interface); + } + + // Disenagaging + public override void OnDisengage() + { + base.OnDisengage(); + + // Hide toolbox window + BuilderPlug.Me.CurveLinedefsForm.Hide(); + } + + // This applies the curves and returns to the base mode + public override void OnAccept() + { + // Create undo + General.Map.UndoRedo.CreateUndo("Curve linedefs"); + + // Go for all selected lines + foreach (Linedef ld in selectedlines) + { + // Make curve for line + List points = GenerateCurve(ld); + if (points.Count > 0) + { + // TODO: We may want some sector create/join code in here + // to allow curves that overlap lines and some geometry merging + + // Go for all points to split the line + Linedef splitline = ld; + for (int i = 0; i < points.Count; i++) + { + // Make vertex + Vertex v = General.Map.Map.CreateVertex(points[i]); + if (v == null) + { + General.Map.UndoRedo.WithdrawUndo(); + return; + } + + // Split the line and move on with this line + splitline = splitline.Split(v); + if (splitline == null) + { + General.Map.UndoRedo.WithdrawUndo(); + return; + } + } + } + } + + // Snap to map format accuracy + General.Map.Map.SnapAllToAccuracy(); + + // Update caches + General.Map.Map.Update(); + General.Map.IsChanged = true; + + // Return to base mode + General.Editing.ChangeMode(General.Editing.PreviousStableMode.Name); + } + + // Redrawing display + public override void OnRedrawDisplay() + { + renderer.RedrawSurface(); + + // Render lines + if (renderer.StartPlotter(true)) + { + renderer.PlotLinedefSet(unselectedlines); + renderer.PlotVerticesSet(General.Map.Map.Vertices); + renderer.Finish(); + } + + // Render things + if (renderer.StartThings(true)) + { + renderer.RenderThingSet(General.Map.Map.Things, 1.0f); + renderer.Finish(); + } + + // Render overlay + if (renderer.StartOverlay(true)) + { + // Go for all selected lines + foreach (Linedef ld in selectedlines) + { + // Make curve for line + List points = GenerateCurve(ld); + if (points.Count > 0) + { + Vector2D p1 = ld.Start.Position; + Vector2D p2 = points[0]; + for (int i = 1; i <= points.Count; i++) + { + // Draw the line + renderer.RenderLine(p1, p2, LINE_THICKNESS, General.Colors.Highlight, true); + + // Next points + p1 = p2; + if (i < points.Count) p2 = points[i]; + } + + // Draw last line + renderer.RenderLine(p2, ld.End.Position, LINE_THICKNESS, General.Colors.Highlight, true); + } + } + renderer.Finish(); + } + + renderer.Present(); + } + + #endregion + } +} diff --git a/Source/Plugins/BuilderModes/ClassicModes/DragGeometryMode.cs b/Source/Plugins/BuilderModes/ClassicModes/DragGeometryMode.cs new file mode 100644 index 0000000..690899c --- /dev/null +++ b/Source/Plugins/BuilderModes/ClassicModes/DragGeometryMode.cs @@ -0,0 +1,442 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using CodeImp.DoomBuilder.Windows; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Geometry; +using System.Drawing; +using CodeImp.DoomBuilder.Editing; + +#endregion + +namespace CodeImp.DoomBuilder.BuilderModes +{ + public abstract class DragGeometryMode : BaseClassicMode + { + #region ================== Constants + + #endregion + + #region ================== Variables + + // Mouse position on map where dragging started + private Vector2D dragstartmappos; + + // Item used as reference for snapping to the grid + protected Vertex dragitem; + private Vector2D dragitemposition; + + // List of old vertex positions + private List oldpositions; + + // List of selected items + protected ICollection selectedverts; + + // List of non-selected items + protected ICollection unselectedverts; + + // List of unstable lines + protected ICollection unstablelines; + + // List of unselected lines + protected ICollection snaptolines; + + // Text labels for all unstable lines + protected LineLengthLabel[] labels; + + // Keep track of view changes + private float lastoffsetx; + private float lastoffsety; + private float lastscale; + + // Options + private bool snaptogrid; // SHIFT to toggle + private bool snaptonearest; // CTRL to enable + + #endregion + + #region ================== Properties + + // Just keep the base mode button checked + public override string EditModeButtonName { get { return General.Editing.PreviousStableMode.Name; } } + + #endregion + + #region ================== Constructor / Disposer + + // Disposer + public override void Dispose() + { + // Not already disposed? + if (!isdisposed) + { + // Clean up + if (labels != null) + foreach (LineLengthLabel l in labels) l.Dispose(); + + // Done + base.Dispose(); + } + } + + #endregion + + #region ================== Methods + + // Constructor to start dragging immediately + protected void StartDrag(Vector2D dragstartmappos) + { + // Initialize + this.dragstartmappos = dragstartmappos; + + Cursor.Current = Cursors.AppStarting; + + // We don't want to record this for undoing while we move the geometry around. + // This will be set back to normal when we're done. + General.Map.UndoRedo.IgnorePropChanges = true; + + // Make list of selected vertices + selectedverts = General.Map.Map.GetMarkedVertices(true); + + // Make list of non-selected vertices + // This will be used for snapping to nearest items + unselectedverts = General.Map.Map.GetMarkedVertices(false); + + // Get the nearest vertex for snapping + dragitem = MapSet.NearestVertex(selectedverts, dragstartmappos); + + // Lines to snap to + snaptolines = General.Map.Map.LinedefsFromMarkedVertices(true, false, false); + + // Make old positions list + // We will use this as reference to move the vertices, or to move them back on cancel + oldpositions = new List(selectedverts.Count); + foreach (Vertex v in selectedverts) oldpositions.Add(v.Position); + + // Also keep old position of the dragged item + dragitemposition = dragitem.Position; + + // Keep view information + lastoffsetx = renderer.OffsetX; + lastoffsety = renderer.OffsetY; + lastscale = renderer.Scale; + + // Make list of unstable lines only + // These will have their length displayed during the drag + unstablelines = MapSet.UnstableLinedefsFromVertices(selectedverts); + + // Make text labels + labels = new LineLengthLabel[unstablelines.Count]; + int index = 0; + foreach (Linedef l in unstablelines) + labels[index++] = new LineLengthLabel(l.Start.Position, l.End.Position); + + Cursor.Current = Cursors.Default; + } + + // This moves the selected geometry relatively + // Returns true when geometry has actually moved + private bool MoveGeometryRelative(Vector2D offset, bool snapgrid, bool snapnearest) + { + Vector2D oldpos = dragitem.Position; + Vector2D anchorpos = dragitemposition + offset; + Vector2D tl, br; + + // don't move if the offset contains invalid data + if (!offset.IsFinite()) return false; + + // Find the outmost vertices + tl = br = oldpositions[0]; + for (int i = 0; i < oldpositions.Count; i++) + { + if (oldpositions[i].x < tl.x) tl.x = (int)oldpositions[i].x; + if (oldpositions[i].x > br.x) br.x = (int)oldpositions[i].x; + if (oldpositions[i].y > tl.y) tl.y = (int)oldpositions[i].y; + if (oldpositions[i].y < br.y) br.y = (int)oldpositions[i].y; + } + + // Snap to nearest? + if (snapnearest) + { + // Find nearest unselected vertex within range + Vertex nv = MapSet.NearestVertexSquareRange(unselectedverts, anchorpos, BuilderPlug.Me.StitchRange / renderer.Scale); + if (nv != null) + { + // Move the dragged item + dragitem.Move(nv.Position); + + // Adjust the offset + offset = nv.Position - dragitemposition; + + // Do not snap to grid! + snapgrid = false; + } + else + { + // Find the nearest unselected line within range + Linedef nl = MapSet.NearestLinedefRange(snaptolines, anchorpos, BuilderPlug.Me.StitchRange / renderer.Scale); + if (nl != null) + { + // Snap to grid? + if (snaptogrid) + { + // Get grid intersection coordinates + List coords = nl.GetGridIntersections(); + + // Find nearest grid intersection + float found_distance = float.MaxValue; + Vector2D found_coord = new Vector2D(); + foreach (Vector2D v in coords) + { + Vector2D delta = anchorpos - v; + if (delta.GetLengthSq() < found_distance) + { + found_distance = delta.GetLengthSq(); + found_coord = v; + } + } + + // Move the dragged item + dragitem.Move(found_coord); + + // Align to line here + offset = found_coord - dragitemposition; + + // Do not snap to grid anymore + snapgrid = false; + } + else + { + // Move the dragged item + dragitem.Move(nl.NearestOnLine(anchorpos)); + + // Align to line here + offset = nl.NearestOnLine(anchorpos) - dragitemposition; + } + } + } + } + + // Snap to grid? + if (snapgrid) + { + // Move the dragged item + dragitem.Move(anchorpos); + + // Snap item to grid + dragitem.SnapToGrid(); + + // Adjust the offset + offset += dragitem.Position - anchorpos; + } + + // Make sure the offset is inside the map boundaries + if (offset.x + tl.x < General.Map.Config.LeftBoundary) offset.x = General.Map.Config.LeftBoundary - tl.x; + if (offset.x + br.x > General.Map.Config.RightBoundary) offset.x = General.Map.Config.RightBoundary - br.x; + if (offset.y + tl.y > General.Map.Config.TopBoundary) offset.y = General.Map.Config.TopBoundary - tl.y; + if (offset.y + br.y < General.Map.Config.BottomBoundary) offset.y = General.Map.Config.BottomBoundary - br.y; + + // Drag item moved? + if (!snapgrid || (dragitem.Position != oldpos)) + { + int i = 0; + + // Move selected geometry + foreach (Vertex v in selectedverts) + { + // Move vertex from old position relative to the + // mouse position change since drag start + v.Move(oldpositions[i] + offset); + + // Next + i++; + } + + // Update labels + int index = 0; + foreach (Linedef l in unstablelines) + labels[index++].Move(l.Start.Position, l.End.Position); + + // Moved + return true; + } + else + { + // No changes + return false; + } + } + + // Cancelled + public override void OnCancel() + { + // Move geometry back to original position + MoveGeometryRelative(new Vector2D(0f, 0f), false, false); + + // Resume normal undo/redo recording + General.Map.UndoRedo.IgnorePropChanges = false; + + // If only a single vertex was selected, deselect it now + if (selectedverts.Count == 1) General.Map.Map.ClearSelectedVertices(); + + // Update cached values + General.Map.Map.Update(); + + // Cancel base class + base.OnCancel(); + + // Return to vertices mode + General.Editing.ChangeMode(General.Editing.PreviousStableMode.Name); + } + + // Mode engages + public override void OnEngage() + { + base.OnEngage(); + EnableAutoPanning(); + renderer.SetPresentation(Presentation.Standard); + } + + // Disenagaging + public override void OnDisengage() + { + base.OnDisengage(); + DisableAutoPanning(); + + // When not cancelled + if (!cancelled) + { + Cursor.Current = Cursors.AppStarting; + + // Move geometry back to original position + MoveGeometryRelative(new Vector2D(0f, 0f), false, false); + + // Resume normal undo/redo recording + General.Map.UndoRedo.IgnorePropChanges = false; + + // Make undo for the dragging + General.Map.UndoRedo.CreateUndo("Drag geometry"); + + // Move selected geometry to final position + MoveGeometryRelative(mousemappos - dragstartmappos, snaptogrid, snaptonearest); + + // Stitch geometry + if (snaptonearest) General.Map.Map.StitchGeometry(); + + // Make corrections for backward linedefs + MapSet.FlipBackwardLinedefs(General.Map.Map.Linedefs); + + // Snap to map format accuracy + General.Map.Map.SnapAllToAccuracy(); + + // Update cached values + General.Map.Map.Update(); + + // Done + Cursor.Current = Cursors.Default; + General.Map.IsChanged = true; + } + } + + // This checks if the view offset/zoom changed and updates the check + protected bool CheckViewChanged() + { + bool viewchanged = false; + + // View changed? + if (renderer.OffsetX != lastoffsetx) viewchanged = true; + if (renderer.OffsetY != lastoffsety) viewchanged = true; + if (renderer.Scale != lastscale) viewchanged = true; + + // Keep view information + lastoffsetx = renderer.OffsetX; + lastoffsety = renderer.OffsetY; + lastscale = renderer.Scale; + + // Return result + return viewchanged; + } + + // This updates the dragging + private void Update() + { + snaptogrid = General.Interface.ShiftState ^ General.Interface.SnapToGrid; + snaptonearest = General.Interface.CtrlState ^ General.Interface.AutoMerge; + + // Move selected geometry + if (MoveGeometryRelative(mousemappos - dragstartmappos, snaptogrid, snaptonearest)) + { + // Update cached values + General.Map.Map.Update(true, false); + + // Redraw + UpdateRedraw(); + renderer.Present(); + //General.Interface.RedrawDisplay(); + } + } + + // This redraws only the required things + protected virtual void UpdateRedraw() + { + } + + // When edit button is released + protected override void OnEditEnd() + { + // Just return to base mode, Disengage will be called automatically. + General.Editing.ChangeMode(General.Editing.PreviousStableMode.Name); + + base.OnEditEnd(); + } + + // Mouse moving + public override void OnMouseMove(MouseEventArgs e) + { + base.OnMouseMove(e); + Update(); + } + // When a key is released + public override void OnKeyUp(KeyEventArgs e) + { + base.OnKeyUp(e); + if ((snaptogrid != (General.Interface.ShiftState ^ General.Interface.SnapToGrid)) || + (snaptonearest != (General.Interface.CtrlState ^ General.Interface.AutoMerge))) Update(); + } + + // When a key is pressed + public override void OnKeyDown(KeyEventArgs e) + { + base.OnKeyDown(e); + if ((snaptogrid != (General.Interface.ShiftState ^ General.Interface.SnapToGrid)) || + (snaptonearest != (General.Interface.CtrlState ^ General.Interface.AutoMerge))) Update(); + } + + #endregion + } +} diff --git a/Source/Plugins/BuilderModes/ClassicModes/DragLinedefsMode.cs b/Source/Plugins/BuilderModes/ClassicModes/DragLinedefsMode.cs new file mode 100644 index 0000000..4d5cb54 --- /dev/null +++ b/Source/Plugins/BuilderModes/ClassicModes/DragLinedefsMode.cs @@ -0,0 +1,187 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using CodeImp.DoomBuilder.Windows; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Geometry; +using System.Drawing; +using CodeImp.DoomBuilder.Editing; + +#endregion + +namespace CodeImp.DoomBuilder.BuilderModes +{ + // No action or button for this mode, it is automatic. + // The EditMode attribute does not have to be specified unless the + // mode must be activated by class name rather than direct instance. + // In that case, just specifying the attribute like this is enough: + // [EditMode] + + [EditMode(DisplayName = "Linedefs", + Volatile = true)] + + public sealed class DragLinedefsMode : DragGeometryMode + { + #region ================== Constants + + #endregion + + #region ================== Variables + + private ICollection selectedlines; + private ICollection unselectedlines; + + #endregion + + #region ================== Properties + + #endregion + + #region ================== Constructor / Disposer + + // Constructor to start dragging immediately + public DragLinedefsMode(Vector2D dragstartmappos) + { + // Mark what we are dragging + General.Map.Map.ClearAllMarks(false); + General.Map.Map.MarkSelectedLinedefs(true, true); + ICollection verts = General.Map.Map.GetVerticesFromLinesMarks(true); + foreach (Vertex v in verts) v.Marked = true; + + // Get line collections + selectedlines = General.Map.Map.GetSelectedLinedefs(true); + unselectedlines = General.Map.Map.GetSelectedLinedefs(false); + + // Initialize + base.StartDrag(dragstartmappos); + + // We have no destructor + GC.SuppressFinalize(this); + } + + // Disposer + public override void Dispose() + { + // Not already disposed? + if (!isdisposed) + { + // Clean up + + // Done + base.Dispose(); + } + } + + #endregion + + #region ================== Methods + + // Mode engages + public override void OnEngage() + { + base.OnEngage(); + } + + // Disenagaging + public override void OnDisengage() + { + // Select vertices from lines selection + General.Map.Map.ClearSelectedVertices(); + ICollection verts = General.Map.Map.GetVerticesFromLinesMarks(true); + foreach (Vertex v in verts) v.Selected = true; + + // Perform normal disengage + base.OnDisengage(); + + // Clear vertex selection + General.Map.Map.ClearSelectedVertices(); + + // When not cancelled + if (!cancelled) + { + // If only a single linedef was selected, deselect it now + if (selectedlines.Count == 1) General.Map.Map.ClearSelectedLinedefs(); + } + } + + // This redraws the display + public override void OnRedrawDisplay() + { + bool viewchanged = CheckViewChanged(); + + renderer.RedrawSurface(); + + UpdateRedraw(); + + if (viewchanged) + { + // Start rendering things + if (renderer.StartThings(true)) + { + renderer.RenderThingSet(General.Map.Map.Things, 1.0f); + renderer.Finish(); + } + } + + renderer.Present(); + } + + // This redraws only the required things + protected override void UpdateRedraw() + { + // Start rendering structure + if (renderer.StartPlotter(true)) + { + // Render lines and vertices + renderer.PlotLinedefSet(unselectedlines); + renderer.PlotLinedefSet(selectedlines); + renderer.PlotVerticesSet(General.Map.Map.Vertices); + + // Draw the dragged item highlighted + // This is important to know, because this item is used + // for snapping to the grid and snapping to nearest items + renderer.PlotVertex(dragitem, ColorCollection.HIGHLIGHT); + + // Done + renderer.Finish(); + } + + // Redraw overlay + if (renderer.StartOverlay(true)) + { + foreach (LineLengthLabel l in labels) + { + renderer.RenderText(l.TextLabel); + } + renderer.Finish(); + } + } + + #endregion + } +} diff --git a/Source/Plugins/BuilderModes/ClassicModes/DragSectorsMode.cs b/Source/Plugins/BuilderModes/ClassicModes/DragSectorsMode.cs new file mode 100644 index 0000000..c769d35 --- /dev/null +++ b/Source/Plugins/BuilderModes/ClassicModes/DragSectorsMode.cs @@ -0,0 +1,193 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using CodeImp.DoomBuilder.Windows; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Geometry; +using System.Drawing; +using CodeImp.DoomBuilder.Editing; + +#endregion + +namespace CodeImp.DoomBuilder.BuilderModes +{ + // No action or button for this mode, it is automatic. + // The EditMode attribute does not have to be specified unless the + // mode must be activated by class name rather than direct instance. + // In that case, just specifying the attribute like this is enough: + // [EditMode] + + [EditMode(DisplayName = "Sectors", + Volatile = true)] + + public sealed class DragSectorsMode : DragGeometryMode + { + #region ================== Constants + + #endregion + + #region ================== Variables + + private ICollection selectedlines; + private ICollection selectedsectors; + + #endregion + + #region ================== Properties + + #endregion + + #region ================== Constructor / Disposer + + // Constructor to start dragging immediately + public DragSectorsMode(Vector2D dragstartmappos) + { + // Mark what we are dragging + General.Map.Map.ClearAllMarks(false); + General.Map.Map.MarkSelectedLinedefs(true, true); + ICollection verts = General.Map.Map.GetVerticesFromLinesMarks(true); + foreach (Vertex v in verts) v.Marked = true; + + // Get selected lines + selectedlines = General.Map.Map.GetSelectedLinedefs(true); + selectedsectors = General.Map.Map.GetSelectedSectors(true); + + // Initialize + base.StartDrag(dragstartmappos); + + // We have no destructor + GC.SuppressFinalize(this); + } + + // Disposer + public override void Dispose() + { + // Not already disposed? + if (!isdisposed) + { + // Clean up + + // Done + base.Dispose(); + } + } + + #endregion + + #region ================== Methods + + // Mode engages + public override void OnEngage() + { + base.OnEngage(); + renderer.SetPresentation(Presentation.Standard); + } + + // Disenagaging + public override void OnDisengage() + { + // Select vertices from lines selection + General.Map.Map.ClearSelectedVertices(); + ICollection verts = General.Map.Map.GetVerticesFromLinesMarks(true); + foreach (Vertex v in verts) v.Selected = true; + + // Perform normal disengage + base.OnDisengage(); + + // Clear vertex selection + General.Map.Map.ClearSelectedVertices(); + + // When not cancelled + if (!cancelled) + { + // If only a single sector was selected, deselect it now + if (selectedsectors.Count == 1) + { + General.Map.Map.ClearSelectedSectors(); + General.Map.Map.ClearSelectedLinedefs(); + } + } + } + + // This redraws the display + public override void OnRedrawDisplay() + { + bool viewchanged = CheckViewChanged(); + + renderer.RedrawSurface(); + + UpdateRedraw(); + + // Redraw things when view changed + if (viewchanged) + { + if (renderer.StartThings(true)) + { + renderer.RenderThingSet(General.Map.Map.Things, 1.0f); + renderer.Finish(); + } + } + + renderer.Present(); + } + + // This redraws only the required things + protected override void UpdateRedraw() + { + // Start rendering + if (renderer.StartPlotter(true)) + { + // Render lines and vertices + renderer.PlotLinedefSet(snaptolines); + renderer.PlotLinedefSet(unstablelines); + renderer.PlotLinedefSet(selectedlines); + renderer.PlotVerticesSet(General.Map.Map.Vertices); + + // Draw the dragged item highlighted + // This is important to know, because this item is used + // for snapping to the grid and snapping to nearest items + renderer.PlotVertex(dragitem, ColorCollection.HIGHLIGHT); + + // Done + renderer.Finish(); + } + + // Redraw overlay + if (renderer.StartOverlay(true)) + { + foreach (LineLengthLabel l in labels) + { + renderer.RenderText(l.TextLabel); + } + renderer.Finish(); + } + } + + #endregion + } +} diff --git a/Source/Plugins/BuilderModes/ClassicModes/DragThingsMode.cs b/Source/Plugins/BuilderModes/ClassicModes/DragThingsMode.cs new file mode 100644 index 0000000..c5b3545 --- /dev/null +++ b/Source/Plugins/BuilderModes/ClassicModes/DragThingsMode.cs @@ -0,0 +1,417 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using CodeImp.DoomBuilder.Windows; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Geometry; +using System.Drawing; +using CodeImp.DoomBuilder.Editing; + +#endregion + +namespace CodeImp.DoomBuilder.BuilderModes +{ + // No action or button for this mode, it is automatic. + // The EditMode attribute does not have to be specified unless the + // mode must be activated by class name rather than direct instance. + // In that case, just specifying the attribute like this is enough: + // [EditMode] + + [EditMode(DisplayName = "Things", + Volatile = true)] + + public sealed class DragThingsMode : BaseClassicMode + { + #region ================== Constants + + #endregion + + #region ================== Variables + + // Mode to return to + private EditMode basemode; + + // Mouse position on map where dragging started + private Vector2D dragstartmappos; + + // Item used as reference for snapping to the grid + private Thing dragitem; + private Vector2D dragitemposition; + + // List of old thing positions + private List oldpositions; + + // List of selected items + private ICollection selectedthings; + + // List of non-selected items + private ICollection unselectedthings; + + // Keep track of view changes + private float lastoffsetx; + private float lastoffsety; + private float lastscale; + + // Options + private bool snaptogrid; // SHIFT to toggle + private bool snaptonearest; // CTRL to enable + + #endregion + + #region ================== Properties + + // Just keep the base mode button checked + public override string EditModeButtonName { get { return basemode.GetType().Name; } } + + internal EditMode BaseMode { get { return basemode; } } + + #endregion + + #region ================== Constructor / Disposer + + // Constructor to start dragging immediately + public DragThingsMode(EditMode basemode, Vector2D dragstartmappos) + { + // Initialize + this.dragstartmappos = dragstartmappos; + this.basemode = basemode; + + Cursor.Current = Cursors.AppStarting; + + // Mark what we are dragging + General.Map.Map.ClearAllMarks(false); + General.Map.Map.MarkSelectedThings(true, true); + + // Get selected things + selectedthings = General.Map.Map.GetMarkedThings(true); + unselectedthings = new List(); + foreach (Thing t in General.Map.ThingsFilter.VisibleThings) if (!t.Marked) unselectedthings.Add(t); + + // Get the nearest thing for snapping + dragitem = MapSet.NearestThing(selectedthings, dragstartmappos); + + // Make old positions list + // We will use this as reference to move the vertices, or to move them back on cancel + oldpositions = new List(selectedthings.Count); + foreach (Thing t in selectedthings) oldpositions.Add(t.Position); + + // Also keep old position of the dragged item + dragitemposition = dragitem.Position; + + // Keep view information + lastoffsetx = renderer.OffsetX; + lastoffsety = renderer.OffsetY; + lastscale = renderer.Scale; + + Cursor.Current = Cursors.Default; + + // We have no destructor + GC.SuppressFinalize(this); + } + + // Disposer + public override void Dispose() + { + // Not already disposed? + if (!isdisposed) + { + // Clean up + + // Done + base.Dispose(); + } + } + + #endregion + + #region ================== Methods + + // This moves the selected things relatively + // Returns true when things has actually moved + private bool MoveThingsRelative(Vector2D offset, bool snapgrid, bool snapnearest) + { + Vector2D oldpos = dragitem.Position; + Thing nearest; + Vector2D tl, br; + + // don't move if the offset contains invalid data + if (!offset.IsFinite()) return false; + + // Find the outmost things + tl = br = oldpositions[0]; + for (int i = 0; i < oldpositions.Count; i++) + { + if (oldpositions[i].x < tl.x) tl.x = (int)oldpositions[i].x; + if (oldpositions[i].x > br.x) br.x = (int)oldpositions[i].x; + if (oldpositions[i].y > tl.y) tl.y = (int)oldpositions[i].y; + if (oldpositions[i].y < br.y) br.y = (int)oldpositions[i].y; + } + + // Snap to nearest? + if (snapnearest) + { + // Find nearest unselected item within selection range + nearest = MapSet.NearestThingSquareRange(unselectedthings, mousemappos, BuilderPlug.Me.StitchRange / renderer.Scale); + if (nearest != null) + { + // Move the dragged item + dragitem.Move((Vector2D)nearest.Position); + + // Adjust the offset + offset = (Vector2D)nearest.Position - dragitemposition; + + // Do not snap to grid! + snapgrid = false; + } + } + + // Snap to grid? + if (snapgrid) + { + // Move the dragged item + dragitem.Move(dragitemposition + offset); + + // Snap item to grid + dragitem.SnapToGrid(); + + // Adjust the offset + offset += (Vector2D)dragitem.Position - (dragitemposition + offset); + } + + // Make sure the offset is inside the map boundaries + if (offset.x + tl.x < General.Map.Config.LeftBoundary) offset.x = General.Map.Config.LeftBoundary - tl.x; + if (offset.x + br.x > General.Map.Config.RightBoundary) offset.x = General.Map.Config.RightBoundary - br.x; + if (offset.y + tl.y > General.Map.Config.TopBoundary) offset.y = General.Map.Config.TopBoundary - tl.y; + if (offset.y + br.y < General.Map.Config.BottomBoundary) offset.y = General.Map.Config.BottomBoundary - br.y; + + // Drag item moved? + if (!snapgrid || ((Vector2D)dragitem.Position != oldpos)) + { + int i = 0; + + // Move selected geometry + foreach (Thing t in selectedthings) + { + // Move vertex from old position relative to the + // mouse position change since drag start + t.Move(oldpositions[i] + offset); + + // Next + i++; + } + + // Moved + return true; + } + else + { + // No changes + return false; + } + } + + // This redraws the display + public override void OnRedrawDisplay() + { + bool viewchanged = CheckViewChanged(); + + if (viewchanged) + { + renderer.RedrawSurface(); + + // Render lines and vertices + if (renderer.StartPlotter(true)) + { + renderer.PlotLinedefSet(General.Map.Map.Linedefs); + renderer.PlotVerticesSet(General.Map.Map.Vertices); + renderer.Finish(); + } + } + + // Render things + UpdateRedraw(); + + renderer.Present(); + } + + // This redraws only changed things + private void UpdateRedraw() + { + // Render things + if (renderer.StartThings(true)) + { + // Render things + renderer.RenderThingSet(General.Map.ThingsFilter.HiddenThings, Presentation.THINGS_HIDDEN_ALPHA); + renderer.RenderThingSet(unselectedthings, 1.0f); + renderer.RenderThingSet(selectedthings, 1.0f); + + // Draw the dragged item highlighted + // This is important to know, because this item is used + // for snapping to the grid and snapping to nearest items + renderer.RenderThing(dragitem, General.Colors.Highlight, 1.0f); + + // Done + renderer.Finish(); + } + } + + // Cancelled + public override void OnCancel() + { + // Move geometry back to original position + MoveThingsRelative(new Vector2D(0f, 0f), false, false); + + // If only a single vertex was selected, deselect it now + if (selectedthings.Count == 1) General.Map.Map.ClearSelectedThings(); + + // Update cached values + General.Map.Map.Update(); + + // Cancel base class + base.OnCancel(); + + // Return to vertices mode + General.Editing.ChangeMode(basemode); + } + + // Mode engages + public override void OnEngage() + { + base.OnEngage(); + renderer.SetPresentation(Presentation.Things); + } + + // Disenagaging + public override void OnDisengage() + { + base.OnDisengage(); + Cursor.Current = Cursors.AppStarting; + + // When not cancelled + if (!cancelled) + { + // Move geometry back to original position + MoveThingsRelative(new Vector2D(0f, 0f), false, false); + + // Make undo for the dragging + General.Map.UndoRedo.CreateUndo("Drag things"); + + // Move selected geometry to final position + MoveThingsRelative(mousemappos - dragstartmappos, snaptogrid, snaptonearest); + + // Snap to map format accuracy + General.Map.Map.SnapAllToAccuracy(); + + // Update cached values + General.Map.Map.Update(false, false); + + // Map is changed + General.Map.IsChanged = true; + } + + // Hide highlight info + General.Interface.HideInfo(); + + // Done + Cursor.Current = Cursors.Default; + } + + // This checks if the view offset/zoom changed and updates the check + private bool CheckViewChanged() + { + bool viewchanged = false; + + // View changed? + if (renderer.OffsetX != lastoffsetx) viewchanged = true; + if (renderer.OffsetY != lastoffsety) viewchanged = true; + if (renderer.Scale != lastscale) viewchanged = true; + + // Keep view information + lastoffsetx = renderer.OffsetX; + lastoffsety = renderer.OffsetY; + lastscale = renderer.Scale; + + // Return result + return viewchanged; + } + + // This updates the dragging + private void Update() + { + snaptogrid = General.Interface.ShiftState ^ General.Interface.SnapToGrid; + snaptonearest = General.Interface.CtrlState; + + // Move selected geometry + if (MoveThingsRelative(mousemappos - dragstartmappos, snaptogrid, snaptonearest)) + { + // Update cached values + //General.Map.Map.Update(true, false); + General.Map.Map.Update(); + + // Redraw + UpdateRedraw(); + renderer.Present(); + //General.Interface.RedrawDisplay(); + } + } + + // When edit button is released + protected override void OnEditEnd() + { + // Just return to vertices mode, geometry will be merged on disengage. + General.Editing.ChangeMode(basemode); + + base.OnEditEnd(); + } + + // Mouse moving + public override void OnMouseMove(MouseEventArgs e) + { + base.OnMouseMove(e); + Update(); + } + + // When a key is released + public override void OnKeyUp(KeyEventArgs e) + { + base.OnKeyUp(e); + if (snaptogrid != General.Interface.ShiftState ^ General.Interface.SnapToGrid) Update(); + if (snaptonearest != General.Interface.CtrlState) Update(); + } + + // When a key is pressed + public override void OnKeyDown(KeyEventArgs e) + { + base.OnKeyDown(e); + if (snaptogrid != General.Interface.ShiftState ^ General.Interface.SnapToGrid) Update(); + if (snaptonearest != General.Interface.CtrlState) Update(); + } + + #endregion + } +} diff --git a/Source/Plugins/BuilderModes/ClassicModes/DragVerticesMode.cs b/Source/Plugins/BuilderModes/ClassicModes/DragVerticesMode.cs new file mode 100644 index 0000000..a02fdb7 --- /dev/null +++ b/Source/Plugins/BuilderModes/ClassicModes/DragVerticesMode.cs @@ -0,0 +1,174 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using CodeImp.DoomBuilder.Windows; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Geometry; +using System.Drawing; +using CodeImp.DoomBuilder.Editing; + +#endregion + +namespace CodeImp.DoomBuilder.BuilderModes +{ + // No action or button for this mode, it is automatic. + // The EditMode attribute does not have to be specified unless the + // mode must be activated by class name rather than direct instance. + // In that case, just specifying the attribute like this is enough: + // [EditMode] + + [EditMode(DisplayName = "Vertices", + Volatile = true)] + + public sealed class DragVerticesMode : DragGeometryMode + { + #region ================== Constants + + #endregion + + #region ================== Variables + + #endregion + + #region ================== Properties + + #endregion + + #region ================== Constructor / Disposer + + // Constructor to start dragging immediately + public DragVerticesMode(Vertex dragitem, Vector2D dragstartmappos) + { + // Mark what we are dragging + General.Map.Map.ClearAllMarks(false); + General.Map.Map.MarkSelectedVertices(true, true); + + // Initialize + base.StartDrag(dragstartmappos); + + // We have no destructor + GC.SuppressFinalize(this); + } + + // Disposer + public override void Dispose() + { + // Not already disposed? + if (!isdisposed) + { + // Clean up + + // Done + base.Dispose(); + } + } + + #endregion + + #region ================== Methods + + // Mode engages + public override void OnEngage() + { + base.OnEngage(); + } + + // Disenagaging + public override void OnDisengage() + { + // Select vertices from marks + General.Map.Map.ClearSelectedVertices(); + General.Map.Map.SelectMarkedVertices(true, true); + + // Perform normal disengage + base.OnDisengage(); + + // When not cancelled + if (!cancelled) + { + // If only a single vertex was selected, deselect it now + if (selectedverts.Count == 1) General.Map.Map.ClearSelectedVertices(); + } + } + + // This redraws the display + public override void OnRedrawDisplay() + { + bool viewchanged = CheckViewChanged(); + + renderer.RedrawSurface(); + + UpdateRedraw(); + + // Redraw things when view changed + if (viewchanged) + { + if (renderer.StartThings(true)) + { + renderer.RenderThingSet(General.Map.Map.Things, 1.0f); + renderer.Finish(); + } + } + + renderer.Present(); + } + + // This redraws only the required things + protected override void UpdateRedraw() + { + // Start rendering + if (renderer.StartPlotter(true)) + { + // Render lines and vertices + renderer.PlotLinedefSet(General.Map.Map.Linedefs); + renderer.PlotVerticesSet(unselectedverts); + renderer.PlotVerticesSet(selectedverts); + + // Draw the dragged item highlighted + // This is important to know, because this item is used + // for snapping to the grid and snapping to nearest items + renderer.PlotVertex(dragitem, ColorCollection.HIGHLIGHT); + + // Done + renderer.Finish(); + } + + // Redraw overlay + if (renderer.StartOverlay(true)) + { + foreach (LineLengthLabel l in labels) + { + renderer.RenderText(l.TextLabel); + } + renderer.Finish(); + } + } + + #endregion + } +} diff --git a/Source/Plugins/BuilderModes/ClassicModes/DrawGeometryMode.cs b/Source/Plugins/BuilderModes/ClassicModes/DrawGeometryMode.cs new file mode 100644 index 0000000..6ca0e19 --- /dev/null +++ b/Source/Plugins/BuilderModes/ClassicModes/DrawGeometryMode.cs @@ -0,0 +1,588 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using CodeImp.DoomBuilder.Windows; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Geometry; +using System.Drawing; +using CodeImp.DoomBuilder.Editing; +using CodeImp.DoomBuilder.Actions; + +#endregion + +namespace CodeImp.DoomBuilder.BuilderModes +{ + [EditMode(DisplayName = "Drawing Mode", + SwitchAction = "drawlinesmode", + Volatile = true, + UseByDefault = true, + Optional = false)] + + public class DrawGeometryMode : BaseClassicMode + { + #region ================== Constants + + private const float LINE_THICKNESS = 0.8f; + + #endregion + + #region ================== Variables + + // Drawing points + private List points; + private List labels; + + // Keep track of view changes + private float lastoffsetx; + private float lastoffsety; + private float lastscale; + + // Options + private bool snaptogrid; // SHIFT to toggle + private bool snaptonearest; // CTRL to enable + + #endregion + + #region ================== Properties + + // Just keep the base mode button checked + public override string EditModeButtonName { get { return General.Editing.PreviousStableMode.Name; } } + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + public DrawGeometryMode() + { + // Initialize + points = new List(); + labels = new List(); + + // No selection in this mode + General.Map.Map.ClearAllSelected(); + General.Map.Map.ClearAllMarks(false); + + // We have no destructor + GC.SuppressFinalize(this); + } + + // Disposer + public override void Dispose() + { + // Not already disposed? + if (!isdisposed) + { + // Clean up + if (labels != null) + foreach (LineLengthLabel l in labels) l.Dispose(); + + // Done + base.Dispose(); + } + } + + #endregion + + #region ================== Methods + + // This checks if the view offset/zoom changed and updates the check + protected bool CheckViewChanged() + { + bool viewchanged = false; + + // View changed? + if (renderer.OffsetX != lastoffsetx) viewchanged = true; + if (renderer.OffsetY != lastoffsety) viewchanged = true; + if (renderer.Scale != lastscale) viewchanged = true; + + // Keep view information + lastoffsetx = renderer.OffsetX; + lastoffsety = renderer.OffsetY; + lastscale = renderer.Scale; + + // Return result + return viewchanged; + } + + // This updates the dragging + private void Update() + { + PixelColor stitchcolor = General.Colors.Highlight; + PixelColor losecolor = General.Colors.Selection; + PixelColor color; + + snaptogrid = General.Interface.ShiftState ^ General.Interface.SnapToGrid; + snaptonearest = General.Interface.CtrlState ^ General.Interface.AutoMerge; + + DrawnVertex lastp = new DrawnVertex(); + DrawnVertex curp = GetCurrentPosition(); + float vsize = ((float)renderer.VertexSize + 1.0f) / renderer.Scale; + float vsizeborder = ((float)renderer.VertexSize + 3.0f) / renderer.Scale; + + // The last label's end must go to the mouse cursor + if (labels.Count > 0) labels[labels.Count - 1].End = curp.pos; + + // Render drawing lines + if (renderer.StartOverlay(true)) + { + // Go for all points to draw lines + if (points.Count > 0) + { + // Render lines + lastp = points[0]; + for (int i = 1; i < points.Count; i++) + { + // Determine line color + if (lastp.stitchline && points[i].stitchline) color = stitchcolor; + else color = losecolor; + + // Render line + renderer.RenderLine(lastp.pos, points[i].pos, LINE_THICKNESS, color, true); + lastp = points[i]; + } + + // Determine line color + if (lastp.stitchline && snaptonearest) color = stitchcolor; + else color = losecolor; + + // Render line to cursor + renderer.RenderLine(lastp.pos, curp.pos, LINE_THICKNESS, color, true); + + // Render vertices + for (int i = 0; i < points.Count; i++) + { + // Determine vertex color + if (points[i].stitch) color = stitchcolor; + else color = losecolor; + + // Render vertex + renderer.RenderRectangleFilled(new RectangleF(points[i].pos.x - vsize, points[i].pos.y - vsize, vsize * 2.0f, vsize * 2.0f), color, true); + } + } + + // Determine point color + if (snaptonearest) color = stitchcolor; + else color = losecolor; + + // Render vertex at cursor + renderer.RenderRectangleFilled(new RectangleF(curp.pos.x - vsize, curp.pos.y - vsize, vsize * 2.0f, vsize * 2.0f), color, true); + + // Go for all labels + foreach (LineLengthLabel l in labels) renderer.RenderText(l.TextLabel); + + // Done + renderer.Finish(); + } + + // Done + renderer.Present(); + } + + // This returns the aligned and snapped draw position + public static DrawnVertex GetCurrentPosition(Vector2D mousemappos, bool snaptonearest, bool snaptogrid, IRenderer2D renderer, List points) + { + DrawnVertex p = new DrawnVertex(); + Vector2D vm = mousemappos; + float vrange = BuilderPlug.Me.StitchRange / renderer.Scale; + + // Snap to nearest? + if (snaptonearest) + { + // Go for all drawn points + foreach (DrawnVertex v in points) + { + if (Vector2D.DistanceSq(mousemappos, v.pos) < (vrange * vrange)) + { + p.pos = v.pos; + p.stitch = true; + p.stitchline = true; + return p; + } + } + + // Try the nearest vertex + Vertex nv = General.Map.Map.NearestVertexSquareRange(mousemappos, vrange); + if (nv != null) + { + p.pos = nv.Position; + p.stitch = true; + p.stitchline = true; + return p; + } + + // Try the nearest linedef + Linedef nl = General.Map.Map.NearestLinedefRange(mousemappos, BuilderPlug.Me.StitchRange / renderer.Scale); + if (nl != null) + { + // Snap to grid? + if (snaptogrid) + { + // Get grid intersection coordinates + List coords = nl.GetGridIntersections(); + + // Find nearest grid intersection + float found_distance = float.MaxValue; + Vector2D found_coord = new Vector2D(); + foreach (Vector2D v in coords) + { + Vector2D delta = mousemappos - v; + if (delta.GetLengthSq() < found_distance) + { + found_distance = delta.GetLengthSq(); + found_coord = v; + } + } + + // Align to the closest grid intersection + p.pos = found_coord; + p.stitch = true; + p.stitchline = true; + return p; + } + else + { + // Aligned to line + p.pos = nl.NearestOnLine(mousemappos); + p.stitch = true; + p.stitchline = true; + return p; + } + } + } + else + { + // Always snap to the first drawn vertex so that the user can finish a complete sector without stitching + if (points.Count > 0) + { + if (Vector2D.DistanceSq(mousemappos, points[0].pos) < (vrange * vrange)) + { + p.pos = points[0].pos; + p.stitch = true; + p.stitchline = false; + return p; + } + } + } + + // if the mouse cursor is outside the map bondaries check if the line between the last set point and the + // mouse cursor intersect any of the boundary lines. If it does, set the position to this intersection + if (points.Count > 0 && + (mousemappos.x < General.Map.Config.LeftBoundary || mousemappos.x > General.Map.Config.RightBoundary || + mousemappos.y > General.Map.Config.TopBoundary || mousemappos.y < General.Map.Config.BottomBoundary)) + { + Line2D dline = new Line2D(mousemappos, points[points.Count - 1].pos); + bool foundintersection = false; + float u = 0.0f; + List blines = new List(); + + // lines for left, top, right and bottom bondaries + blines.Add(new Line2D(General.Map.Config.LeftBoundary, General.Map.Config.BottomBoundary, General.Map.Config.LeftBoundary, General.Map.Config.TopBoundary)); + blines.Add(new Line2D(General.Map.Config.LeftBoundary, General.Map.Config.TopBoundary, General.Map.Config.RightBoundary, General.Map.Config.TopBoundary)); + blines.Add(new Line2D(General.Map.Config.RightBoundary, General.Map.Config.TopBoundary, General.Map.Config.RightBoundary, General.Map.Config.BottomBoundary)); + blines.Add(new Line2D(General.Map.Config.RightBoundary, General.Map.Config.BottomBoundary, General.Map.Config.LeftBoundary, General.Map.Config.BottomBoundary)); + + // check for intersections with boundaries + for (int i = 0; i < blines.Count; i++) + { + if (!foundintersection) + { + // only check for intersection if the last set point is not on the + // line we are checking against + if (blines[i].GetSideOfLine(points[points.Count - 1].pos) != 0.0f) + { + foundintersection = blines[i].GetIntersection(dline, out u); + } + } + } + + // if there was no intersection set the position to the last set point + if (!foundintersection) + vm = points[points.Count - 1].pos; + else + vm = dline.GetCoordinatesAt(u); + + } + + + // Snap to grid? + if (snaptogrid) + { + // Aligned to grid + p.pos = General.Map.Grid.SnappedToGrid(vm); + + // special handling + if (p.pos.x > General.Map.Config.RightBoundary) p.pos.x = General.Map.Config.RightBoundary; + if (p.pos.y < General.Map.Config.BottomBoundary) p.pos.y = General.Map.Config.BottomBoundary; + p.stitch = snaptonearest; + p.stitchline = snaptonearest; + return p; + } + else + { + // Normal position + p.pos = vm; + p.stitch = snaptonearest; + p.stitchline = snaptonearest; + return p; + } + } + + // This gets the aligned and snapped draw position + private DrawnVertex GetCurrentPosition() + { + return GetCurrentPosition(mousemappos, snaptonearest, snaptogrid, renderer, points); + } + + // This draws a point at a specific location + public bool DrawPointAt(DrawnVertex p) + { + return DrawPointAt(p.pos, p.stitch, p.stitchline); + } + + // This draws a point at a specific location + public bool DrawPointAt(Vector2D pos, bool stitch, bool stitchline) + { + if (pos.x < General.Map.Config.LeftBoundary || pos.x > General.Map.Config.RightBoundary || + pos.y > General.Map.Config.TopBoundary || pos.y < General.Map.Config.BottomBoundary) + return false; + + DrawnVertex newpoint = new DrawnVertex(); + newpoint.pos = pos; + newpoint.stitch = stitch; + newpoint.stitchline = stitchline; + points.Add(newpoint); + labels.Add(new LineLengthLabel()); + labels[labels.Count - 1].Start = newpoint.pos; + if (labels.Count > 1) labels[labels.Count - 2].End = newpoint.pos; + Update(); + + // Check if point stitches with the first + if ((points.Count > 1) && points[points.Count - 1].stitch) + { + Vector2D p1 = points[0].pos; + Vector2D p2 = points[points.Count - 1].pos; + Vector2D delta = p1 - p2; + if ((Math.Abs(delta.x) <= 0.001f) && (Math.Abs(delta.y) <= 0.001f)) + { + // Finish drawing + FinishDraw(); + } + } + + return true; + } + + #endregion + + #region ================== Events + + public override void OnHelp() + { + General.ShowHelp("e_drawgeometry.html"); + } + + // Engaging + public override void OnEngage() + { + base.OnEngage(); + EnableAutoPanning(); + renderer.SetPresentation(Presentation.Standard); + + // Set cursor + General.Interface.SetCursor(Cursors.Cross); + } + + // Disengaging + public override void OnDisengage() + { + base.OnDisengage(); + DisableAutoPanning(); + } + + // Cancelled + public override void OnCancel() + { + // Cancel base class + base.OnCancel(); + + // Return to original mode + General.Editing.ChangeMode(General.Editing.PreviousStableMode.Name); + } + + // Accepted + public override void OnAccept() + { + Cursor.Current = Cursors.AppStarting; + + General.Settings.FindDefaultDrawSettings(); + + // When points have been drawn + if (points.Count > 0) + { + // Make undo for the draw + General.Map.UndoRedo.CreateUndo("Line draw"); + + // Make an analysis and show info + string[] adjectives = new string[] + { "beautiful", "lovely", "romantic", "stylish", "cheerful", "comical", + "awesome", "accurate", "adorable", "adventurous", "attractive", "cute", + "elegant", "glamorous", "gorgeous", "handsome", "magnificent", "unusual", + "outstanding", "mysterious", "amusing", "charming", "fantastic", "jolly" }; + string word = adjectives[points.Count % adjectives.Length]; + word = (points.Count > adjectives.Length) ? "very " + word : word; + string a = ((word[0] == 'a') || (word[0] == 'e') || (word[0] == 'o')) ? "an " : "a "; + General.Interface.DisplayStatus(StatusType.Action, "Created " + a + word + " drawing."); + + // Make the drawing + if (!Tools.DrawLines(points)) + { + // Drawing failed + // NOTE: I have to call this twice, because the first time only cancels this volatile mode + General.Map.UndoRedo.WithdrawUndo(); + General.Map.UndoRedo.WithdrawUndo(); + return; + } + + // Snap to map format accuracy + General.Map.Map.SnapAllToAccuracy(); + + // Clear selection + General.Map.Map.ClearAllSelected(); + + // Update cached values + General.Map.Map.Update(); + + // Edit new sectors? + List newsectors = General.Map.Map.GetMarkedSectors(true); + if (BuilderPlug.Me.EditNewSector && (newsectors.Count > 0)) + General.Interface.ShowEditSectors(newsectors); + + // Update the used textures + General.Map.Data.UpdateUsedTextures(); + + // Map is changed + General.Map.IsChanged = true; + } + + // Done + Cursor.Current = Cursors.Default; + + // Return to original mode + General.Editing.ChangeMode(General.Editing.PreviousStableMode.Name); + } + + // This redraws the display + public override void OnRedrawDisplay() + { + renderer.RedrawSurface(); + + // Render lines + if (renderer.StartPlotter(true)) + { + renderer.PlotLinedefSet(General.Map.Map.Linedefs); + renderer.PlotVerticesSet(General.Map.Map.Vertices); + renderer.Finish(); + } + + // Render things + if (renderer.StartThings(true)) + { + renderer.RenderThingSet(General.Map.Map.Things, 1.0f); + renderer.Finish(); + } + + // Normal update + Update(); + } + + // Mouse moving + public override void OnMouseMove(MouseEventArgs e) + { + base.OnMouseMove(e); + Update(); + } + + // When a key is released + public override void OnKeyUp(KeyEventArgs e) + { + base.OnKeyUp(e); + if ((snaptogrid != (General.Interface.ShiftState ^ General.Interface.SnapToGrid)) || + (snaptonearest != (General.Interface.CtrlState ^ General.Interface.AutoMerge))) Update(); + } + + // When a key is pressed + public override void OnKeyDown(KeyEventArgs e) + { + base.OnKeyDown(e); + if ((snaptogrid != (General.Interface.ShiftState ^ General.Interface.SnapToGrid)) || + (snaptonearest != (General.Interface.CtrlState ^ General.Interface.AutoMerge))) Update(); + } + + #endregion + + #region ================== Actions + + // Drawing a point + [BeginAction("drawpoint")] + public void DrawPoint() + { + // Mouse inside window? + if (General.Interface.MouseInDisplay) + { + DrawnVertex newpoint = GetCurrentPosition(); + + if (!DrawPointAt(newpoint)) General.Interface.DisplayStatus(StatusType.Warning, "Failed to draw point: outside of map boundaries."); + } + } + + // Remove a point + [BeginAction("removepoint")] + public void RemovePoint() + { + if (points.Count > 0) points.RemoveAt(points.Count - 1); + if (labels.Count > 0) + { + labels[labels.Count - 1].Dispose(); + labels.RemoveAt(labels.Count - 1); + } + + Update(); + } + + // Finish drawing + [BeginAction("finishdraw")] + public void FinishDraw() + { + // Accept the changes + General.Editing.AcceptMode(); + } + + #endregion + } +} diff --git a/Source/Plugins/BuilderModes/ClassicModes/EditSelectionMode.cs b/Source/Plugins/BuilderModes/ClassicModes/EditSelectionMode.cs new file mode 100644 index 0000000..1cd01dc --- /dev/null +++ b/Source/Plugins/BuilderModes/ClassicModes/EditSelectionMode.cs @@ -0,0 +1,1545 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using CodeImp.DoomBuilder.Windows; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Geometry; +using CodeImp.DoomBuilder.Editing; +using CodeImp.DoomBuilder.Actions; +using CodeImp.DoomBuilder.Types; +using CodeImp.DoomBuilder.Config; +using System.Drawing; +using CodeImp.DoomBuilder.Controls; + +#endregion + +namespace CodeImp.DoomBuilder.BuilderModes +{ + [EditMode(DisplayName = "Edit Selection Mode", + SwitchAction = "editselectionmode", // Action name used to switch to this mode + Volatile = true, + UseByDefault = true, + Optional = false)] + + public class EditSelectionMode : BaseClassicMode + { + #region ================== Enums + + private enum ModifyMode : int + { + None, + Dragging, + Resizing, + Rotating + } + + private enum Grip : int + { + None, + Main, + SizeN, + SizeS, + SizeE, + SizeW, + RotateLT, + RotateRT, + RotateRB, + RotateLB + } + + #endregion + + #region ================== Constants + + private const float GRIP_SIZE = 9.0f; + private const float ZERO_SIZE_ADDITION = 20.0f; + private const byte RECTANGLE_ALPHA = 60; + private const byte EXTENSION_LINE_ALPHA = 150; + private readonly Cursor[] RESIZE_CURSORS = { Cursors.SizeNS, Cursors.SizeNWSE, Cursors.SizeWE, Cursors.SizeNESW }; + + #endregion + + #region ================== Variables + + // Modes + private bool modealreadyswitching = false; + private bool pasting = false; + private PasteOptions pasteoptions; + + // Docker + private EditSelectionPanel panel; + private Docker docker; + + // Highlighted vertex + private MapElement highlighted; + private Vector2D highlightedpos; + + // Selection + private ICollection selectedvertices; + private ICollection selectedthings; + private ICollection selectedlines; + private List vertexpos; + private List thingpos; + private List thingangle; + private ICollection unselectedvertices; + private ICollection unselectedlines; + + // Modification + private float rotation; + private Vector2D offset; + private Vector2D size; + private Vector2D baseoffset; + private Vector2D basesize; + private bool linesflipped; + + // Modifying Modes + private ModifyMode mode; + private Vector2D dragoffset; + private Vector2D resizefilter; + private Vector2D resizevector; + private Vector2D edgevector; + private Line2D resizeaxis; + private int stickcorner; + private float rotategripangle; + private bool autopanning; + + // Rectangle components + private Vector2D[] originalcorners; // lefttop, righttop, rightbottom, leftbottom + private Vector2D[] corners; + private FlatVertex[] cornerverts; + private RectangleF[] resizegrips; // top, right, bottom, left + private RectangleF[] rotategrips; // lefttop, righttop, rightbottom, leftbottom + private Line2D extensionline; + + // Options + private bool snaptogrid; // SHIFT to toggle + private bool snaptonearest; // CTRL to enable + + #endregion + + #region ================== Properties + + public override object HighlightedObject { get { return highlighted; } } + + // Just keep the base mode button checked + public override string EditModeButtonName { get { return General.Editing.PreviousStableMode.Name; } } + + public bool Pasting { get { return pasting; } set { pasting = value; } } + public PasteOptions PasteOptions { get { return pasteoptions; } set { pasteoptions = value.Copy(); } } + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + public EditSelectionMode() + { + // Initialize + mode = ModifyMode.None; + } + + // Disposer + public override void Dispose() + { + // Not already disposed? + if (!isdisposed) + { + // Clean up + + // Dispose base + base.Dispose(); + } + } + + #endregion + + #region ================== Methods + + // The following functions set different properties and update + + public void SetAbsPosX(float posx) + { + offset.x = posx; + UpdateAllChanges(); + } + + public void SetAbsPosY(float posy) + { + offset.y = posy; + UpdateAllChanges(); + } + + public void SetRelPosX(float posx) + { + offset.x = posx + baseoffset.x; + UpdateAllChanges(); + } + + public void SetRelPosY(float posy) + { + offset.y = posy + baseoffset.y; + UpdateAllChanges(); + } + + public void SetAbsSizeX(float sizex) + { + size.x = sizex; + UpdateAllChanges(); + } + + public void SetAbsSizeY(float sizey) + { + size.y = sizey; + UpdateAllChanges(); + } + + public void SetRelSizeX(float sizex) + { + size.x = basesize.x * (sizex / 100.0f); + UpdateAllChanges(); + } + + public void SetRelSizeY(float sizey) + { + size.y = basesize.y * (sizey / 100.0f); + UpdateAllChanges(); + } + + public void SetAbsRotation(float absrot) + { + rotation = absrot; + UpdateAllChanges(); + } + + // This updates all after changes were made + private void UpdateAllChanges() + { + UpdateGeometry(); + UpdateRectangleComponents(); + General.Map.Map.Update(); + General.Interface.RedrawDisplay(); + } + + // This returns the position of the highlighted item + private Vector2D GetHighlightedPosition() + { + if (highlighted is Vertex) + return (highlighted as Vertex).Position; + else if (highlighted is Thing) + return (highlighted as Thing).Position; + else + throw new Exception("Highlighted element type is not supported."); + } + + // This highlights a new vertex + protected void Highlight(MapElement h) + { + // Undraw previous highlight + if ((highlighted != null) && !highlighted.IsDisposed) + { + if (highlighted is Vertex) + { + if (renderer.StartPlotter(false)) + { + renderer.PlotVertex((highlighted as Vertex), renderer.DetermineVertexColor((highlighted as Vertex))); + renderer.Finish(); + } + } + else + { + if (renderer.StartThings(false)) + { + renderer.RenderThing((highlighted as Thing), renderer.DetermineThingColor((highlighted as Thing)), 1.0f); + renderer.Finish(); + } + } + } + + // Set new highlight + highlighted = h; + + // Render highlighted item + if ((highlighted != null) && !highlighted.IsDisposed) + { + if (highlighted is Vertex) + { + if (renderer.StartPlotter(false)) + { + renderer.PlotVertex((highlighted as Vertex), ColorCollection.HIGHLIGHT); + renderer.Finish(); + } + } + else + { + if (renderer.StartThings(false)) + { + renderer.RenderThing((highlighted as Thing), General.Colors.Highlight, 1.0f); + renderer.Finish(); + } + } + } + + // Done + renderer.Present(); + } + + // This updates the selection + private void Update() + { + // Not in any modifying mode? + if (mode == ModifyMode.None) + { + // Check what grip the mouse is over + // and change cursor accordingly + Grip mousegrip = CheckMouseGrip(); + switch (mousegrip) + { + case Grip.Main: + + // Find the nearest vertex within highlight range + Vertex v = MapSet.NearestVertex(selectedvertices, mousemappos); + + // Find the nearest thing within range + Thing t = MapSet.NearestThing(selectedthings, mousemappos); + + // Highlight the one that is closer + if ((v != null) && (t != null)) + { + if (v.DistanceToSq(mousemappos) < t.DistanceToSq(mousemappos)) + { + if (v != highlighted) Highlight(v); + } + else + { + if (t != highlighted) Highlight(t); + } + } + else if (v != null) + { + if (v != highlighted) Highlight(v); + } + else + { + if (t != highlighted) Highlight(t); + } + + General.Interface.SetCursor(Cursors.Hand); + break; + + case Grip.RotateLB: + case Grip.RotateLT: + case Grip.RotateRB: + case Grip.RotateRT: + Highlight(null); + General.Interface.SetCursor(Cursors.Cross); + break; + + case Grip.SizeE: + case Grip.SizeS: + case Grip.SizeW: + case Grip.SizeN: + + // Pick the best matching cursor depending on rotation and side + float resizeangle = rotation; + if ((mousegrip == Grip.SizeE) || (mousegrip == Grip.SizeW)) resizeangle += Angle2D.PIHALF; + resizeangle = Angle2D.Normalized(resizeangle); + if (resizeangle > Angle2D.PI) resizeangle -= Angle2D.PI; + resizeangle = Math.Abs(resizeangle + Angle2D.PI / 8.000001f); + int cursorindex = (int)Math.Floor((resizeangle / Angle2D.PI) * 4.0f) % 4; + General.Interface.SetCursor(RESIZE_CURSORS[cursorindex]); + Highlight(null); + break; + + default: + Highlight(null); + General.Interface.SetCursor(Cursors.Default); + break; + } + } + else + { + Vector2D snappedmappos = mousemappos; + bool dosnaptogrid = snaptogrid; + + // Options + snaptogrid = General.Interface.ShiftState ^ General.Interface.SnapToGrid; + snaptonearest = General.Interface.CtrlState ^ General.Interface.AutoMerge; + + // Change to crosshair cursor so we can clearly see around the mouse cursor + General.Interface.SetCursor(Cursors.Cross); + + // Check what modifying mode we are in + switch (mode) + { + // Dragging + case ModifyMode.Dragging: + + // Change offset without snapping + offset = mousemappos - dragoffset; + + // Calculate transformed position of highlighted vertex + Vector2D transformedpos = TransformedPoint(highlightedpos); + + // Snap to nearest vertex? + if (snaptonearest && (highlighted != null)) + { + float vrange = BuilderPlug.Me.StitchRange / renderer.Scale; + + // Try the nearest vertex + Vertex nv = MapSet.NearestVertexSquareRange(unselectedvertices, transformedpos, vrange); + if (nv != null) + { + // Change offset to snap to target + offset += nv.Position - transformedpos; + dosnaptogrid = false; + } + else + { + // Find the nearest unselected line within range + Linedef nl = MapSet.NearestLinedefRange(unselectedlines, transformedpos, BuilderPlug.Me.StitchRange / renderer.Scale); + if (nl != null) + { + // Snap to grid? + if (dosnaptogrid) + { + // Get grid intersection coordinates + List coords = nl.GetGridIntersections(); + + // Find nearest grid intersection + float found_distance = float.MaxValue; + Vector2D found_pos = new Vector2D(float.NaN, float.NaN); + foreach (Vector2D v in coords) + { + Vector2D dist = transformedpos - v; + if (dist.GetLengthSq() < found_distance) + { + // Found a better match + found_distance = dist.GetLengthSq(); + found_pos = v; + + // Do not snap to grid anymore + dosnaptogrid = false; + } + } + + // Found something? + if (!float.IsNaN(found_pos.x)) + { + // Change offset to snap to target + offset += found_pos - transformedpos; + } + } + else + { + // Change offset to snap onto the line + offset += nl.NearestOnLine(transformedpos) - transformedpos; + } + } + } + } + + // Snap to grid? + if (dosnaptogrid && (highlighted != null)) + { + // Change offset to align to grid + offset += General.Map.Grid.SnappedToGrid(transformedpos) - transformedpos; + } + + // Update + UpdateGeometry(); + UpdateRectangleComponents(); + General.Interface.RedrawDisplay(); + break; + + // Resizing + case ModifyMode.Resizing: + + // Snap to nearest vertex? + if (snaptonearest) + { + float vrange = BuilderPlug.Me.StitchRange / renderer.Scale; + + // Try the nearest vertex + Vertex nv = MapSet.NearestVertexSquareRange(unselectedvertices, snappedmappos, vrange); + if (nv != null) + { + snappedmappos = nv.Position; + dosnaptogrid = false; + } + } + + // Snap to grid? + if (dosnaptogrid) + { + // Aligned to grid + snappedmappos = General.Map.Grid.SnappedToGrid(snappedmappos); + } + + // Keep corner position + Vector2D oldcorner = corners[stickcorner]; + + // Change size with the scale from the ruler + float scale = resizeaxis.GetNearestOnLine(snappedmappos); + size = (basesize * resizefilter) * scale + size * (1.0f - resizefilter); + + // Adjust corner position + Vector2D newcorner = TransformedPoint(originalcorners[stickcorner]); + offset -= newcorner - oldcorner; + + // Show the extension line so that the user knows what it is aligning to + Vector2D sizefiltered = (size * resizefilter); + float sizelength = sizefiltered.x + sizefiltered.y; + Line2D edgeline = new Line2D(resizeaxis.v1 + resizevector * sizelength, resizeaxis.v1 + resizevector * sizelength - edgevector); + float nearestonedge = edgeline.GetNearestOnLine(snappedmappos); + if (nearestonedge > 0.5f) + extensionline = new Line2D(edgeline.v1, snappedmappos); + else + extensionline = new Line2D(edgeline.v2, snappedmappos); + + // Update + UpdateGeometry(); + UpdateRectangleComponents(); + General.Interface.RedrawDisplay(); + break; + + // Rotating + case ModifyMode.Rotating: + + // Get angle from mouse to center + Vector2D center = offset + size * 0.5f; + Vector2D delta = snappedmappos - center; + rotation = delta.GetAngle() - rotategripangle; + + // Snap rotation to grip? + if (dosnaptogrid) + { + // We make 8 vectors that the rotation can snap to + float founddistance = float.MaxValue; + float foundrotation = rotation; + for (int i = 0; i < 8; i++) + { + // Make the vectors + float angle = (float)i * Angle2D.PI * 0.25f; + Vector2D gridvec = Vector2D.FromAngle(angle); + Vector3D rotvec = Vector2D.FromAngle(rotation); + + // Check distance + float dist = 2.0f - Vector2D.DotProduct(gridvec, rotvec); + if (dist < founddistance) + { + foundrotation = angle; + founddistance = dist; + } + } + + // Keep rotation + rotation = foundrotation; + } + + // Update + UpdateGeometry(); + UpdateRectangleComponents(); + General.Interface.RedrawDisplay(); + break; + } + } + } + + // This checks and returns the grip the mouse pointer is in + private Grip CheckMouseGrip() + { + if (PointInRectF(resizegrips[0], mousemappos)) + return Grip.SizeN; + else if (PointInRectF(resizegrips[2], mousemappos)) + return Grip.SizeS; + else if (PointInRectF(resizegrips[1], mousemappos)) + return Grip.SizeE; + else if (PointInRectF(resizegrips[3], mousemappos)) + return Grip.SizeW; + else if (PointInRectF(rotategrips[0], mousemappos)) + return Grip.RotateLT; + else if (PointInRectF(rotategrips[1], mousemappos)) + return Grip.RotateRT; + else if (PointInRectF(rotategrips[2], mousemappos)) + return Grip.RotateRB; + else if (PointInRectF(rotategrips[3], mousemappos)) + return Grip.RotateLB; + else if (Tools.PointInPolygon(corners, mousemappos)) + return Grip.Main; + else + return Grip.None; + } + + // This applies the current rotation and resize to a point + private Vector2D TransformedPoint(Vector2D p) + { + // Resize + p = (p - baseoffset) * (size / basesize) + baseoffset; + + // Rotate + Vector2D center = baseoffset + size * 0.5f; + Vector2D po = p - center; + p = po.GetRotated(rotation); + p += center; + + // Translate + p += offset - baseoffset; + + return p; + } + + // This checks if a point is in a rect + private bool PointInRectF(RectangleF rect, Vector2D point) + { + return (point.x >= rect.Left) && (point.x <= rect.Right) && (point.y >= rect.Top) && (point.y <= rect.Bottom); + } + + // This updates the values in the panel + private void UpdatePanel() + { + Vector2D relsize = (size / basesize) * 100.0f; + if (panel != null) + panel.ShowCurrentValues(offset, offset - baseoffset, size, relsize, rotation); + } + + // This moves all things and vertices to match the current transformation + private void UpdateGeometry() + { + int index = 0; + foreach (Vertex v in selectedvertices) + { + v.Move(TransformedPoint(vertexpos[index++])); + } + + index = 0; + foreach (Thing t in selectedthings) + { + t.Rotate(Angle2D.Normalized(thingangle[index] + rotation)); + t.Move(TransformedPoint(thingpos[index++])); + } + + // This checks if the lines should be flipped + bool shouldbeflipped = (size.x < 0.0f) ^ (size.y < 0.0f); + if (shouldbeflipped != linesflipped) FlipLinedefs(); + + UpdatePanel(); + General.Map.Map.Update(true, false); + } + + // This updates the selection rectangle components + private void UpdateRectangleComponents() + { + float gripsize = GRIP_SIZE / renderer.Scale; + PixelColor rectcolor = General.Colors.Highlight.WithAlpha(RECTANGLE_ALPHA); + + // Original (untransformed) corners + originalcorners = new Vector2D[4]; + originalcorners[0] = new Vector2D(baseoffset.x, baseoffset.y); + originalcorners[1] = new Vector2D(baseoffset.x + basesize.x, baseoffset.y); + originalcorners[2] = new Vector2D(baseoffset.x + basesize.x, baseoffset.y + basesize.y); + originalcorners[3] = new Vector2D(baseoffset.x, baseoffset.y + basesize.y); + + // Corners + corners = new Vector2D[4]; + for (int i = 0; i < 4; i++) + corners[i] = TransformedPoint(originalcorners[i]); + + // Vertices + cornerverts = new FlatVertex[6]; + for (int i = 0; i < 6; i++) + { + cornerverts[i] = new FlatVertex(); + cornerverts[i].z = 1.0f; + cornerverts[i].c = rectcolor.ToInt(); + } + cornerverts[0].x = corners[0].x; + cornerverts[0].y = corners[0].y; + cornerverts[1].x = corners[1].x; + cornerverts[1].y = corners[1].y; + cornerverts[2].x = corners[2].x; + cornerverts[2].y = corners[2].y; + cornerverts[3].x = corners[0].x; + cornerverts[3].y = corners[0].y; + cornerverts[4].x = corners[2].x; + cornerverts[4].y = corners[2].y; + cornerverts[5].x = corners[3].x; + cornerverts[5].y = corners[3].y; + + // Middle points between corners + Vector2D middle01 = corners[0] + (corners[1] - corners[0]) * 0.5f; + Vector2D middle12 = corners[1] + (corners[2] - corners[1]) * 0.5f; + Vector2D middle23 = corners[2] + (corners[3] - corners[2]) * 0.5f; + Vector2D middle30 = corners[3] + (corners[0] - corners[3]) * 0.5f; + + // Resize grips + resizegrips = new RectangleF[4]; + resizegrips[0] = new RectangleF(middle01.x - gripsize * 0.5f, + middle01.y - gripsize * 0.5f, + gripsize, gripsize); + resizegrips[1] = new RectangleF(middle12.x - gripsize * 0.5f, + middle12.y - gripsize * 0.5f, + gripsize, gripsize); + resizegrips[2] = new RectangleF(middle23.x - gripsize * 0.5f, + middle23.y - gripsize * 0.5f, + gripsize, gripsize); + resizegrips[3] = new RectangleF(middle30.x - gripsize * 0.5f, + middle30.y - gripsize * 0.5f, + gripsize, gripsize); + + // Rotate grips + rotategrips = new RectangleF[4]; + rotategrips[0] = new RectangleF(corners[0].x - gripsize * 0.5f, + corners[0].y - gripsize * 0.5f, + gripsize, gripsize); + rotategrips[1] = new RectangleF(corners[1].x - gripsize * 0.5f, + corners[1].y - gripsize * 0.5f, + gripsize, gripsize); + rotategrips[2] = new RectangleF(corners[2].x - gripsize * 0.5f, + corners[2].y - gripsize * 0.5f, + gripsize, gripsize); + rotategrips[3] = new RectangleF(corners[3].x - gripsize * 0.5f, + corners[3].y - gripsize * 0.5f, + gripsize, gripsize); + } + + // This flips all linedefs in the selection (used for mirroring) + private void FlipLinedefs() + { + // Flip linedefs + foreach (Linedef ld in selectedlines) + ld.FlipVertices(); + + // Done + linesflipped = !linesflipped; + } + + #endregion + + #region ================== Events + + public override void OnHelp() + { + General.ShowHelp("e_editselection.html"); + } + + // Mode engages + public override void OnEngage() + { + base.OnEngage(); + + bool autodrag = (pasting && mouseinside && BuilderPlug.Me.AutoDragOnPaste); + + // Add toolbar buttons + General.Interface.AddButton(BuilderPlug.Me.MenusForm.FlipSelectionH); + General.Interface.AddButton(BuilderPlug.Me.MenusForm.FlipSelectionV); + + // Add docker + panel = new EditSelectionPanel(this); + docker = new Docker("editselection", "Edit Selection", panel); + General.Interface.AddDocker(docker); + General.Interface.SelectDocker(docker); + + // We don't want to record this for undoing while we move the geometry around. + // This will be set back to normal when we're done. + General.Map.UndoRedo.IgnorePropChanges = true; + + // Convert geometry selection + General.Map.Map.ClearAllMarks(false); + General.Map.Map.MarkSelectedVertices(true, true); + General.Map.Map.MarkSelectedThings(true, true); + General.Map.Map.MarkSelectedLinedefs(true, true); + General.Map.Map.MarkSelectedSectors(true, true); + ICollection verts = General.Map.Map.GetVerticesFromLinesMarks(true); + foreach (Vertex v in verts) v.Marked = true; + ICollection sects = General.Map.Map.GetSelectedSectors(true); + foreach (Sector s in sects) + { + foreach (Sidedef sd in s.Sidedefs) + { + sd.Line.Marked = true; + sd.Line.Start.Marked = true; + sd.Line.End.Marked = true; + } + } + selectedvertices = General.Map.Map.GetMarkedVertices(true); + selectedthings = General.Map.Map.GetMarkedThings(true); + unselectedvertices = General.Map.Map.GetMarkedVertices(false); + + // Make sure everything is selected so that it turns up red + foreach (Vertex v in selectedvertices) v.Selected = true; + ICollection markedlines = General.Map.Map.LinedefsFromMarkedVertices(false, true, false); + foreach (Linedef l in markedlines) l.Selected = true; + selectedlines = General.Map.Map.LinedefsFromMarkedVertices(false, true, false); + unselectedlines = General.Map.Map.LinedefsFromMarkedVertices(true, false, false); + + // Array to keep original coordinates + vertexpos = new List(selectedvertices.Count); + thingpos = new List(selectedthings.Count); + thingangle = new List(selectedthings.Count); + + // A selection must be made! + if ((selectedvertices.Count > 0) || (selectedthings.Count > 0)) + { + // Initialize offset and size + offset.x = float.MaxValue; + offset.y = float.MaxValue; + Vector2D right; + right.x = float.MinValue; + right.y = float.MinValue; + + foreach (Vertex v in selectedvertices) + { + // Find left-top and right-bottom + if (v.Position.x < offset.x) offset.x = v.Position.x; + if (v.Position.y < offset.y) offset.y = v.Position.y; + if (v.Position.x > right.x) right.x = v.Position.x; + if (v.Position.y > right.y) right.y = v.Position.y; + + // Keep original coordinates + vertexpos.Add(v.Position); + } + + foreach (Thing t in selectedthings) + { + // Find left-top and right-bottom + if ((t.Position.x - t.Size) < offset.x) offset.x = t.Position.x - t.Size; + if ((t.Position.y - t.Size) < offset.y) offset.y = t.Position.y - t.Size; + if ((t.Position.x + t.Size) > right.x) right.x = t.Position.x + t.Size; + if ((t.Position.y + t.Size) > right.y) right.y = t.Position.y + t.Size; + + // Keep original coordinates + thingpos.Add(t.Position); + thingangle.Add(t.Angle); + } + + // Calculate size + size = right - offset; + + // If the width of a dimension is zero, add a little + if (Math.Abs(size.x) < 1.0f) + { + size.x += ZERO_SIZE_ADDITION; + offset.x -= ZERO_SIZE_ADDITION / 2; + } + + if (Math.Abs(size.y) < 1.0f) + { + size.y += ZERO_SIZE_ADDITION; + offset.y -= ZERO_SIZE_ADDITION / 2; + } + + basesize = size; + baseoffset = offset; + + // When pasting, we want to move the geometry so it is visible + if (pasting) + { + // Mouse in screen? + if (mouseinside) + { + offset = mousemappos - size / 2; + } + else + { + Vector2D viewmappos = new Vector2D(renderer.OffsetX, renderer.OffsetY); + offset = viewmappos - size / 2; + } + + UpdateGeometry(); + General.Map.Data.UpdateUsedTextures(); + + if (!autodrag) + General.Map.Map.Update(); + } + + // Set presentation + if (selectedthings.Count > 0) + renderer.SetPresentation(Presentation.Things); + else + renderer.SetPresentation(Presentation.Standard); + + // Update + panel.ShowOriginalValues(baseoffset, basesize); + UpdateRectangleComponents(); + UpdatePanel(); + Update(); + + // When pasting and mouse is in screen, drag selection immediately + if (autodrag) OnSelectBegin(); + } + else + { + General.Interface.MessageBeep(MessageBeepType.Default); + + // Cancel now + General.Editing.CancelMode(); + } + } + + // Cancel mode + public override void OnCancel() + { + base.OnCancel(); + + // Paste operation? + if (pasting) + { + // Resume normal undo/redo recording + General.Map.UndoRedo.IgnorePropChanges = false; + + // Remove the geometry + int index = 0; + foreach (Vertex v in selectedvertices) + v.Dispose(); + + index = 0; + foreach (Thing t in selectedthings) + t.Dispose(); + + // Withdraw the undo + if (General.Map.UndoRedo.NextUndo != null) + General.Map.UndoRedo.WithdrawUndo(); + } + else + { + // Reset geometry in original position + int index = 0; + foreach (Vertex v in selectedvertices) + v.Move(vertexpos[index++]); + + index = 0; + foreach (Thing t in selectedthings) + { + t.Rotate(thingangle[index]); + t.Move(thingpos[index++]); + } + + // Resume normal undo/redo recording + General.Map.UndoRedo.IgnorePropChanges = false; + } + + General.Map.Map.Update(true, true); + + // Return to previous stable mode + General.Editing.ChangeMode(General.Editing.PreviousStableMode.Name); + } + + // When accepted + public override void OnAccept() + { + base.OnAccept(); + + // Anything to do? + if ((selectedthings.Count > 0) || (selectedvertices.Count > 0)) + { + Vector2D tl = new Vector2D(General.Map.Config.RightBoundary, General.Map.Config.BottomBoundary); + Vector2D br = new Vector2D(General.Map.Config.LeftBoundary, General.Map.Config.RightBoundary); + + foreach (Vertex v in selectedvertices) + { + if (v.Position.x < tl.x) tl.x = (int)v.Position.x; + if (v.Position.x > br.x) br.x = (int)v.Position.x; + if (v.Position.y > tl.y) tl.y = (int)v.Position.y; + if (v.Position.y < br.y) br.y = (int)v.Position.y; + } + + foreach (Thing t in selectedthings) + { + if (t.Position.x < tl.x) tl.x = (int)t.Position.x; + if (t.Position.x > br.x) br.x = (int)t.Position.x; + if (t.Position.y > tl.y) tl.y = (int)t.Position.y; + if (t.Position.y < br.y) br.y = (int)t.Position.y; + } + + // Check if the selection is outside the map boundaries + if (tl.x < General.Map.Config.LeftBoundary || br.x > General.Map.Config.RightBoundary || + tl.y > General.Map.Config.TopBoundary || br.y < General.Map.Config.BottomBoundary) + { + General.Interface.DisplayStatus(StatusType.Warning, "Error: selection out of map boundaries."); + + // If we're in the process of switching to another mode, reset to selection + // to its old position + if (modealreadyswitching == true) + { + // Reset geometry in original position + int index = 0; + foreach (Vertex v in selectedvertices) + v.Move(vertexpos[index++]); + + index = 0; + foreach (Thing t in selectedthings) + { + t.Rotate(thingangle[index]); + t.Move(thingpos[index++]); + } + + // Resume normal undo/redo recording + General.Map.UndoRedo.IgnorePropChanges = false; + + General.Map.Map.Update(true, true); + } + + return; + } + + Cursor.Current = Cursors.AppStarting; + + if (!pasting) + { + // Reset geometry in original position to create an undo + if (linesflipped) FlipLinedefs(); // Flip linedefs back if they were flipped + int index = 0; + foreach (Vertex v in selectedvertices) + v.Move(vertexpos[index++]); + + index = 0; + foreach (Thing t in selectedthings) + { + t.Rotate(thingangle[index]); + t.Move(thingpos[index++]); + } + General.Map.Map.Update(true, true); + + // Make undo + General.Map.UndoRedo.CreateUndo("Edit selection"); + } + + // Resume normal undo/redo recording + General.Map.UndoRedo.IgnorePropChanges = false; + + // Mark selected geometry + General.Map.Map.ClearAllMarks(false); + General.Map.Map.MarkAllSelectedGeometry(true, true, true, true, false); + + // Move geometry to new position + UpdateGeometry(); + General.Map.Map.Update(true, true); + + // When pasting, we want to join with the parent sector + // where the sidedefs are referencing a virtual sector + if (pasting) + { + Sector parent = null; + Sector vsector = null; + General.Settings.FindDefaultDrawSettings(); + + // Go for all sidedes in the new geometry + List newsides = General.Map.Map.GetMarkedSidedefs(true); + for (int i = 0; i < newsides.Count; i++) + { + Sidedef s = newsides[i]; + + // Connected to a virtual sector? + if (s.Marked && s.Sector.Fields.ContainsKey(MapSet.VirtualSectorField)) + { + bool joined = false; + + // Keep reference to virtual sector + vsector = s.Sector; + + // Not virtual on both sides? + // Pascal 3-1-08: I can't remember why I have this check here, but it causes problems when + // pasting a single linedef that refers to the same sector on both sides (the line then + // loses both its sidedefs because it doesn't join any sector) + //if((s.Other != null) && !s.Other.Sector.Fields.ContainsKey(MapSet.VirtualSectorField)) + { + Sidedef joinsidedef = null; + + // Find out in which sector this was pasted + Vector2D testpoint = s.Line.GetSidePoint(!s.IsFront); + Linedef nl = MapSet.NearestLinedef(General.Map.Map.GetMarkedLinedefs(false), testpoint); + if (nl != null) + { + if (nl.SideOfLine(testpoint) <= 0) + joinsidedef = nl.Front; + else + joinsidedef = nl.Back; + + // Join? + if (joinsidedef != null) + { + // Join! + s.SetSector(joinsidedef.Sector); + s.Marked = false; + joined = true; + + // If we have no parent sector yet, then this is it! + if (parent == null) parent = joinsidedef.Sector; + } + } + } + + // Not joined any sector? + if (!joined) + { + Linedef l = s.Line; + + // Remove the sidedef + s.Dispose(); + + // Correct the linedef + if ((l.Front == null) && (l.Back != null)) + { + l.FlipVertices(); + l.FlipSidedefs(); + } + + // Correct the sided flags + l.ApplySidedFlags(); + } + } + } + + // Do we have a virtual and parent sector? + if ((vsector != null) && (parent != null)) + { + // Adjust the floor and ceiling heights of all new sectors + if (pasteoptions.AdjustHeights) + { + ICollection newsectors = General.Map.Map.GetMarkedSectors(true); + foreach (Sector s in newsectors) + { + s.CeilHeight += parent.CeilHeight - vsector.CeilHeight; + s.FloorHeight += parent.FloorHeight - vsector.FloorHeight; + } + } + } + + // Remove any virtual sectors + General.Map.Map.RemoveVirtualSectors(); + } + + // Stitch geometry + if (snaptonearest) General.Map.Map.StitchGeometry(); + + // Make corrections for backward linedefs + MapSet.FlipBackwardLinedefs(General.Map.Map.Linedefs); + + // Snap to map format accuracy + General.Map.Map.SnapAllToAccuracy(); + + // Update cached values + General.Map.Data.UpdateUsedTextures(); + General.Map.Map.Update(); + + // Make normal selection + General.Map.Map.ClearAllSelected(); + foreach (Vertex v in selectedvertices) v.Selected = true; + foreach (Linedef l in selectedlines) { l.Start.Selected = true; l.End.Selected = true; } + foreach (Thing t in selectedthings) t.Selected = true; + General.Map.Map.SelectionType = SelectionType.Vertices | SelectionType.Things; + + // Done + selectedvertices = new List(); + selectedthings = new List(); + Cursor.Current = Cursors.Default; + General.Map.IsChanged = true; + } + + if (!modealreadyswitching) + { + // Return to previous stable mode + General.Editing.ChangeMode(General.Editing.PreviousStableMode.Name); + } + } + + // Mode disengages + public override void OnDisengage() + { + base.OnDisengage(); + + // Remove toolbar buttons + General.Interface.RemoveButton(BuilderPlug.Me.MenusForm.FlipSelectionH); + General.Interface.RemoveButton(BuilderPlug.Me.MenusForm.FlipSelectionV); + + // Remove docker + General.Interface.RemoveDocker(docker); + panel.Dispose(); + panel = null; + + // When not cancelled manually, we assume it is accepted + if (!cancelled) + { + modealreadyswitching = true; + this.OnAccept(); + } + + // Update + General.Map.ThingsFilter.Update(); + General.Interface.RedrawDisplay(); + + // Hide highlight info + General.Interface.HideInfo(); + General.Interface.SetCursor(Cursors.Default); + } + + // This redraws the display + public override void OnRedrawDisplay() + { + UpdateRectangleComponents(); + + renderer.RedrawSurface(); + + // Render lines + if (renderer.StartPlotter(true)) + { + renderer.PlotLinedefSet(General.Map.Map.Linedefs); + renderer.PlotVerticesSet(General.Map.Map.Vertices); + if (highlighted is Vertex) renderer.PlotVertex((highlighted as Vertex), ColorCollection.HIGHLIGHT); + renderer.Finish(); + } + + // Render things + if (renderer.StartThings(true)) + { + renderer.RenderThingSet(General.Map.ThingsFilter.HiddenThings, Presentation.THINGS_HIDDEN_ALPHA); + renderer.RenderThingSet(General.Map.ThingsFilter.VisibleThings, 1.0f); + if (highlighted is Thing) renderer.RenderThing((highlighted as Thing), General.Colors.Highlight, 1.0f); + renderer.Finish(); + } + + // Render selection + if (renderer.StartOverlay(true)) + { + // Rectangle + PixelColor rectcolor = General.Colors.Highlight.WithAlpha(RECTANGLE_ALPHA); + renderer.RenderGeometry(cornerverts, null, true); + renderer.RenderLine(corners[0], corners[1], 4, rectcolor, true); + renderer.RenderLine(corners[1], corners[2], 4, rectcolor, true); + renderer.RenderLine(corners[2], corners[3], 4, rectcolor, true); + renderer.RenderLine(corners[3], corners[0], 4, rectcolor, true); + + // Extension line + if (extensionline.GetLengthSq() > 0.0f) + renderer.RenderLine(extensionline.v1, extensionline.v2, 1, General.Colors.Indication.WithAlpha(EXTENSION_LINE_ALPHA), true); + + // Grips + for (int i = 0; i < 4; i++) + { + renderer.RenderRectangleFilled(resizegrips[i], General.Colors.Background, true); + renderer.RenderRectangle(resizegrips[i], 2, General.Colors.Highlight, true); + renderer.RenderRectangleFilled(rotategrips[i], General.Colors.Background, true); + renderer.RenderRectangle(rotategrips[i], 2, General.Colors.Indication, true); + } + + renderer.Finish(); + } + + renderer.Present(); + } + + // Mouse moves + public override void OnMouseMove(MouseEventArgs e) + { + base.OnMouseMove(e); + + Update(); + } + + // Mouse leaves the display + public override void OnMouseLeave(EventArgs e) + { + base.OnMouseLeave(e); + + // Reset cursor + General.Interface.SetCursor(Cursors.Default); + } + + // When edit button is pressed + protected override void OnEditBegin() + { + base.OnEditBegin(); + OnSelectBegin(); + } + + // When edit button is released + protected override void OnEditEnd() + { + base.OnEditEnd(); + OnSelectEnd(); + } + + // When select button is pressed + protected override void OnSelectBegin() + { + base.OnSelectBegin(); + + if (mode != ModifyMode.None) return; + + // Used in many cases: + Vector2D center = offset + size * 0.5f; + Vector2D delta; + + // Check what grip the mouse is over + switch (CheckMouseGrip()) + { + // Drag main rectangle + case Grip.Main: + + // Find the original position of the highlighted element + if (highlighted is Vertex) + { + int index = 0; + foreach (Vertex v in selectedvertices) + { + if (v == highlighted) highlightedpos = vertexpos[index]; + index++; + } + } + else if (highlighted is Thing) + { + int index = 0; + foreach (Thing t in selectedthings) + { + if (t == highlighted) highlightedpos = thingpos[index]; + index++; + } + } + + dragoffset = mousemappos - offset; + mode = ModifyMode.Dragging; + + EnableAutoPanning(); + autopanning = true; + break; + + // Resize + case Grip.SizeN: + + // The resize vector is a unit vector in the direction of the resize. + // We multiply this with the sign of the current size, because the + // corners may be reversed when the selection is flipped. + resizevector = corners[1] - corners[2]; + resizevector = resizevector.GetNormal() * Math.Sign(size.y); + + // The edgevector is a vector with length and direction of the edge perpendicular to the resizevector + edgevector = corners[1] - corners[0]; + + // Make the resize axis. This is a line with the length and direction + // of basesize used to calculate the resize percentage. + resizeaxis = new Line2D(corners[2], corners[2] + resizevector * basesize.y); + + // Original axis filter + resizefilter = new Vector2D(0.0f, 1.0f); + + // This is the corner that must stay in the same position + stickcorner = 2; + + Highlight(null); + mode = ModifyMode.Resizing; + + EnableAutoPanning(); + autopanning = true; + break; + + // Resize + case Grip.SizeE: + // See description above + resizevector = corners[1] - corners[0]; + resizevector = resizevector.GetNormal() * Math.Sign(size.x); + edgevector = corners[1] - corners[2]; + resizeaxis = new Line2D(corners[0], corners[0] + resizevector * basesize.x); + resizefilter = new Vector2D(1.0f, 0.0f); + stickcorner = 0; + Highlight(null); + mode = ModifyMode.Resizing; + + EnableAutoPanning(); + autopanning = true; + break; + + // Resize + case Grip.SizeS: + // See description above + resizevector = corners[2] - corners[1]; + resizevector = resizevector.GetNormal() * Math.Sign(size.y); + edgevector = corners[2] - corners[3]; + resizeaxis = new Line2D(corners[1], corners[1] + resizevector * basesize.y); + resizefilter = new Vector2D(0.0f, 1.0f); + stickcorner = 0; + Highlight(null); + mode = ModifyMode.Resizing; + + EnableAutoPanning(); + autopanning = true; + break; + + // Resize + case Grip.SizeW: + // See description above + resizevector = corners[0] - corners[1]; + resizevector = resizevector.GetNormal() * Math.Sign(size.x); + edgevector = corners[0] - corners[3]; + resizeaxis = new Line2D(corners[1], corners[1] + resizevector * basesize.x); + resizefilter = new Vector2D(1.0f, 0.0f); + stickcorner = 1; + Highlight(null); + mode = ModifyMode.Resizing; + + EnableAutoPanning(); + autopanning = true; + break; + + // Rotate + case Grip.RotateLB: + delta = corners[3] - center; + rotategripangle = delta.GetAngle() - rotation; + Highlight(null); + mode = ModifyMode.Rotating; + + EnableAutoPanning(); + autopanning = true; + break; + + // Rotate + case Grip.RotateLT: + delta = corners[0] - center; + rotategripangle = delta.GetAngle() - rotation; + Highlight(null); + mode = ModifyMode.Rotating; + + EnableAutoPanning(); + autopanning = true; + break; + + // Rotate + case Grip.RotateRB: + delta = corners[2] - center; + rotategripangle = delta.GetAngle() - rotation; + Highlight(null); + mode = ModifyMode.Rotating; + + EnableAutoPanning(); + autopanning = true; + break; + + // Rotate + case Grip.RotateRT: + delta = corners[1] - center; + rotategripangle = delta.GetAngle() - rotation; + Highlight(null); + mode = ModifyMode.Rotating; + + EnableAutoPanning(); + autopanning = true; + break; + + // Outside the selection? + default: + // Accept and be done with it + General.Editing.AcceptMode(); + break; + } + } + + // When selected button is released + protected override void OnSelectEnd() + { + base.OnSelectEnd(); + + // Remove extension line + extensionline = new Line2D(); + + if (autopanning) + { + DisableAutoPanning(); + autopanning = false; + } + + // No modifying mode + mode = ModifyMode.None; + + // Redraw + General.Map.Map.Update(); + General.Interface.RedrawDisplay(); + } + + // When a key is released + public override void OnKeyUp(KeyEventArgs e) + { + base.OnKeyUp(e); + if ((snaptogrid != (General.Interface.ShiftState ^ General.Interface.SnapToGrid)) || + (snaptonearest != (General.Interface.CtrlState ^ General.Interface.AutoMerge))) Update(); + } + + // When a key is pressed + public override void OnKeyDown(KeyEventArgs e) + { + base.OnKeyDown(e); + if ((snaptogrid != (General.Interface.ShiftState ^ General.Interface.SnapToGrid)) || + (snaptonearest != (General.Interface.CtrlState ^ General.Interface.AutoMerge))) Update(); + } + + + + #endregion + + #region ================== Actions + + // This clears the selection + [BeginAction("clearselection", BaseAction = true)] + public void ClearSelection() + { + // Accept changes + General.Editing.AcceptMode(); + General.Map.Map.ClearAllSelected(); + } + + // Flip vertically + [BeginAction("flipselectionv")] + public void FlipVertically() + { + // Flip the selection + offset.y += size.y; + size.y = -size.y; + + // Update + UpdateGeometry(); + UpdateRectangleComponents(); + General.Map.Map.Update(); + General.Interface.RedrawDisplay(); + } + + // Flip horizontally + [BeginAction("flipselectionh")] + public void FlipHorizontally() + { + // Flip the selection + offset.x += size.x; + size.x = -size.x; + + // Update + UpdateGeometry(); + UpdateRectangleComponents(); + General.Map.Map.Update(); + General.Interface.RedrawDisplay(); + } + + #endregion + } +} diff --git a/Source/Plugins/BuilderModes/ClassicModes/ErrorCheckMode.cs b/Source/Plugins/BuilderModes/ClassicModes/ErrorCheckMode.cs new file mode 100644 index 0000000..b063859 --- /dev/null +++ b/Source/Plugins/BuilderModes/ClassicModes/ErrorCheckMode.cs @@ -0,0 +1,165 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using CodeImp.DoomBuilder.Windows; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Geometry; +using System.Drawing; +using CodeImp.DoomBuilder.Editing; + +#endregion + +namespace CodeImp.DoomBuilder.BuilderModes +{ + [EditMode(DisplayName = "Map Analysis Mode", + SwitchAction = "errorcheckmode", + ButtonImage = "MapAnalysisMode.png", + ButtonOrder = 200, + ButtonGroup = "002_tools", + Volatile = true, + UseByDefault = true)] + + public sealed class ErrorCheckMode : BaseClassicMode + { + #region ================== Constants + + #endregion + + #region ================== Variables + + #endregion + + #region ================== Properties + + #endregion + + #region ================== Constructor / Disposer + + #endregion + + #region ================== Events + + public override void OnHelp() + { + General.ShowHelp("e_mapanalysis.html"); + } + + // Cancelled + public override void OnCancel() + { + // Cancel base class + base.OnCancel(); + + // Return to base mode + General.Editing.ChangeMode(General.Editing.PreviousStableMode.Name); + } + + // Mode engages + public override void OnEngage() + { + base.OnEngage(); + renderer.SetPresentation(Presentation.Standard); + + // Save selection as marks + General.Map.Map.ClearAllMarks(false); + General.Map.Map.MarkAllSelectedGeometry(true, false, false, false, false); + General.Map.Map.ClearAllSelected(); + General.Map.Map.SelectionType = SelectionType.All; + + // Show toolbox window + BuilderPlug.Me.ErrorCheckForm.Show((Form)General.Interface); + } + + // Disenagaging + public override void OnDisengage() + { + base.OnDisengage(); + + // Hide object info + General.Interface.HideInfo(); + + // Restore selection + General.Map.Map.SelectMarkedGeometry(true, true); + General.Map.Map.ClearAllMarks(false); + + // Hide toolbox window + BuilderPlug.Me.ErrorCheckForm.CloseWindow(); + } + + // This applies the curves and returns to the base mode + public override void OnAccept() + { + // Snap to map format accuracy + General.Map.Map.SnapAllToAccuracy(); + + // Update caches + General.Map.Map.Update(); + General.Map.IsChanged = true; + + // Return to base mode + General.Editing.ChangeMode(General.Editing.PreviousStableMode.Name); + } + + // Redrawing display + public override void OnRedrawDisplay() + { + // Get the selection + ErrorResult selection = BuilderPlug.Me.ErrorCheckForm.SelectedResult; + + renderer.RedrawSurface(); + + // Render lines + if (renderer.StartPlotter(true)) + { + renderer.PlotLinedefSet(General.Map.Map.Linedefs); + renderer.PlotVerticesSet(General.Map.Map.Vertices); + if (selection != null) selection.PlotSelection(renderer); + renderer.Finish(); + } + + // Render things + if (renderer.StartThings(true)) + { + renderer.RenderThingSet(General.Map.Map.Things, 1.0f); + if (selection != null) selection.RenderThingsSelection(renderer); + renderer.Finish(); + } + + // Render overlay + if (renderer.StartOverlay(true)) + { + if (selection != null) selection.RenderOverlaySelection(renderer); + renderer.Finish(); + } + + renderer.Present(); + } + + #endregion + } +} diff --git a/Source/Plugins/BuilderModes/ClassicModes/FindReplaceMode.cs b/Source/Plugins/BuilderModes/ClassicModes/FindReplaceMode.cs new file mode 100644 index 0000000..f7b9da3 --- /dev/null +++ b/Source/Plugins/BuilderModes/ClassicModes/FindReplaceMode.cs @@ -0,0 +1,171 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using CodeImp.DoomBuilder.Windows; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Geometry; +using System.Drawing; +using CodeImp.DoomBuilder.Editing; + +#endregion + +namespace CodeImp.DoomBuilder.BuilderModes +{ + [EditMode(DisplayName = "Find & Replace Mode", + SwitchAction = "findmode", + ButtonImage = "FindMode.png", + ButtonOrder = 100, + ButtonGroup = "002_tools", + Volatile = true, + UseByDefault = true)] + + public sealed class FindReplaceMode : BaseClassicMode + { + #region ================== Constants + + #endregion + + #region ================== Variables + + #endregion + + #region ================== Properties + + #endregion + + #region ================== Constructor / Disposer + + #endregion + + #region ================== Events + + public override void OnHelp() + { + General.ShowHelp("e_findreplace.html"); + } + + // Cancelled + public override void OnCancel() + { + // Cancel base class + base.OnCancel(); + + // Return to base mode + General.Editing.ChangeMode(General.Editing.PreviousStableMode.Name); + } + + // Mode engages + public override void OnEngage() + { + base.OnEngage(); + renderer.SetPresentation(Presentation.Standard); + General.Map.Map.SelectionType = SelectionType.All; + + // Select linedefs by sectors + foreach (Linedef ld in General.Map.Map.Linedefs) + { + if (ld.Selected == false) + { + bool front, back; + if (ld.Front != null) front = ld.Front.Sector.Selected; else front = false; + if (ld.Back != null) back = ld.Back.Sector.Selected; else back = false; + ld.Selected = front | back; + } + } + + // Show toolbox window + BuilderPlug.Me.FindReplaceForm.Show((Form)General.Interface); + } + + // Disenagaging + public override void OnDisengage() + { + base.OnDisengage(); + + // Hide object info + General.Interface.HideInfo(); + + // Hide toolbox window + BuilderPlug.Me.FindReplaceForm.Hide(); + } + + // This applies the curves and returns to the base mode + public override void OnAccept() + { + // Snap to map format accuracy + General.Map.Map.SnapAllToAccuracy(); + + // Update caches + General.Map.Map.Update(); + General.Map.IsChanged = true; + + // Return to base mode + General.Editing.ChangeMode(General.Editing.PreviousStableMode.Name); + } + + // Redrawing display + public override void OnRedrawDisplay() + { + // Get the selection + FindReplaceObject[] selection = BuilderPlug.Me.FindReplaceForm.GetSelection(); + + renderer.RedrawSurface(); + + // Render lines + if (renderer.StartPlotter(true)) + { + renderer.PlotLinedefSet(General.Map.Map.Linedefs); + if (BuilderPlug.Me.FindReplaceForm.Finder != null) + BuilderPlug.Me.FindReplaceForm.Finder.PlotSelection(renderer, selection); + renderer.PlotVerticesSet(General.Map.Map.Vertices); + renderer.Finish(); + } + + // Render things + if (renderer.StartThings(true)) + { + renderer.RenderThingSet(General.Map.Map.Things, 1.0f); + if (BuilderPlug.Me.FindReplaceForm.Finder != null) + BuilderPlug.Me.FindReplaceForm.Finder.RenderThingsSelection(renderer, selection); + renderer.Finish(); + } + + // Render overlay + if (renderer.StartOverlay(true)) + { + if (BuilderPlug.Me.FindReplaceForm.Finder != null) + BuilderPlug.Me.FindReplaceForm.Finder.RenderOverlaySelection(renderer, selection); + renderer.Finish(); + } + + renderer.Present(); + } + + #endregion + } +} diff --git a/Source/Plugins/BuilderModes/ClassicModes/LinedefsMode.cs b/Source/Plugins/BuilderModes/ClassicModes/LinedefsMode.cs new file mode 100644 index 0000000..e453206 --- /dev/null +++ b/Source/Plugins/BuilderModes/ClassicModes/LinedefsMode.cs @@ -0,0 +1,911 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using CodeImp.DoomBuilder.Windows; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Geometry; +using CodeImp.DoomBuilder.Editing; +using CodeImp.DoomBuilder.Actions; +using CodeImp.DoomBuilder.Types; +using CodeImp.DoomBuilder.Config; + +#endregion + +namespace CodeImp.DoomBuilder.BuilderModes +{ + [EditMode(DisplayName = "Linedefs Mode", + SwitchAction = "linedefsmode", // Action name used to switch to this mode + ButtonImage = "LinesMode.png", // Image resource name for the button + ButtonOrder = int.MinValue + 100, // Position of the button (lower is more to the left) + ButtonGroup = "000_editing", + UseByDefault = true, + SafeStartMode = true)] + + public class LinedefsMode : BaseClassicMode + { + #region ================== Constants + + #endregion + + #region ================== Variables + + // Highlighted item + private Linedef highlighted; + private Association[] association = new Association[Linedef.NUM_ARGS]; + private Association highlightasso = new Association(); + + // Interface + private bool editpressed; + + #endregion + + #region ================== Properties + + public override object HighlightedObject { get { return highlighted; } } + + #endregion + + #region ================== Constructor / Disposer + + #endregion + + #region ================== Methods + + // This highlights a new item + protected void Highlight(Linedef l) + { + bool completeredraw = false; + LinedefActionInfo action = null; + + // Often we can get away by simply undrawing the previous + // highlight and drawing the new highlight. But if associations + // are or were drawn we need to redraw the entire display. + + // Previous association highlights something? + if ((highlighted != null) && (highlighted.Tag > 0)) completeredraw = true; + + // Set highlight association + if (l != null) + highlightasso.Set(l.Tag, UniversalType.LinedefTag); + else + highlightasso.Set(0, 0); + + // New association highlights something? + if ((l != null) && (l.Tag > 0)) completeredraw = true; + + // Use the line tag to highlight sectors (Doom style) + if (General.Map.Config.LineTagIndicatesSectors) + { + if (l != null) + { + association[0].Set(l.Tag, UniversalType.SectorTag); + if (General.Map.FormatInterface.InDoom64Mode) // villsa + association[1].Set(l.Tag, UniversalType.ThingTag); + } + else + association[0].Set(0, 0); + } + else + { + if (l != null) + { + // Check if we can find the linedefs action + if ((l.Action > 0) && General.Map.Config.LinedefActions.ContainsKey(l.Action)) + action = General.Map.Config.LinedefActions[l.Action]; + } + + // Determine linedef associations + for (int i = 0; i < Linedef.NUM_ARGS; i++) + { + // Previous association highlights something? + if ((association[i].type == UniversalType.SectorTag) || + (association[i].type == UniversalType.LinedefTag) || + (association[i].type == UniversalType.ThingTag)) completeredraw = true; + + // Make new association + if (action != null) + association[i].Set(l.Args[i], action.Args[i].Type); + else + association[i].Set(0, 0); + + // New association highlights something? + if ((association[i].type == UniversalType.SectorTag) || + (association[i].type == UniversalType.LinedefTag) || + (association[i].type == UniversalType.ThingTag)) completeredraw = true; + } + } + + // If we're changing associations, then we + // need to redraw the entire display + if (completeredraw) + { + // Set new highlight and redraw completely + highlighted = l; + General.Interface.RedrawDisplay(); + } + else + { + // Update display + if (renderer.StartPlotter(false)) + { + // Undraw previous highlight + if ((highlighted != null) && !highlighted.IsDisposed) + { + renderer.PlotLinedef(highlighted, renderer.DetermineLinedefColor(highlighted)); + renderer.PlotVertex(highlighted.Start, renderer.DetermineVertexColor(highlighted.Start)); + renderer.PlotVertex(highlighted.End, renderer.DetermineVertexColor(highlighted.End)); + } + + // Set new highlight + highlighted = l; + + // Render highlighted item + if ((highlighted != null) && !highlighted.IsDisposed) + { + renderer.PlotLinedef(highlighted, General.Colors.Highlight); + renderer.PlotVertex(highlighted.Start, renderer.DetermineVertexColor(highlighted.Start)); + renderer.PlotVertex(highlighted.End, renderer.DetermineVertexColor(highlighted.End)); + } + + // Done + renderer.Finish(); + renderer.Present(); + } + } + + // Show highlight info + if ((highlighted != null) && !highlighted.IsDisposed) + General.Interface.ShowLinedefInfo(highlighted); + else + General.Interface.HideInfo(); + } + + #endregion + + #region ================== Events + + public override void OnHelp() + { + General.ShowHelp("e_linedefs.html"); + } + + // Cancel mode + public override void OnCancel() + { + base.OnCancel(); + + // Return to this mode + General.Editing.ChangeMode(new LinedefsMode()); + } + + // Mode engages + public override void OnEngage() + { + base.OnEngage(); + renderer.SetPresentation(Presentation.Standard); + + // Add toolbar buttons + General.Interface.AddButton(BuilderPlug.Me.MenusForm.CopyProperties); + General.Interface.AddButton(BuilderPlug.Me.MenusForm.PasteProperties); + General.Interface.AddButton(BuilderPlug.Me.MenusForm.SeparatorCopyPaste); + General.Interface.AddButton(BuilderPlug.Me.MenusForm.CurveLinedefs); + + // Convert geometry selection to linedefs selection + General.Map.Map.ConvertSelection(SelectionType.Linedefs); + } + + // Mode disengages + public override void OnDisengage() + { + base.OnDisengage(); + + // Remove toolbar buttons + General.Interface.RemoveButton(BuilderPlug.Me.MenusForm.CopyProperties); + General.Interface.RemoveButton(BuilderPlug.Me.MenusForm.PasteProperties); + General.Interface.RemoveButton(BuilderPlug.Me.MenusForm.SeparatorCopyPaste); + General.Interface.RemoveButton(BuilderPlug.Me.MenusForm.CurveLinedefs); + + // Going to EditSelectionMode? + if (General.Editing.NewMode is EditSelectionMode) + { + // Not pasting anything? + EditSelectionMode editmode = (General.Editing.NewMode as EditSelectionMode); + if (!editmode.Pasting) + { + // No selection made? But we have a highlight! + if ((General.Map.Map.GetSelectedLinedefs(true).Count == 0) && (highlighted != null)) + { + // Make the highlight the selection + highlighted.Selected = true; + } + } + } + + // Hide highlight info + General.Interface.HideInfo(); + } + + // This redraws the display + public override void OnRedrawDisplay() + { + renderer.RedrawSurface(); + + // Render lines + if (renderer.StartPlotter(true)) + { + renderer.PlotLinedefSet(General.Map.Map.Linedefs); + for (int i = 0; i < Linedef.NUM_ARGS; i++) BuilderPlug.Me.PlotAssociations(renderer, association[i]); + if ((highlighted != null) && !highlighted.IsDisposed) + { + BuilderPlug.Me.PlotReverseAssociations(renderer, highlightasso); + renderer.PlotLinedef(highlighted, General.Colors.Highlight); + } + renderer.PlotVerticesSet(General.Map.Map.Vertices); + renderer.Finish(); + } + + // Render things + if (renderer.StartThings(true)) + { + renderer.RenderThingSet(General.Map.ThingsFilter.HiddenThings, Presentation.THINGS_HIDDEN_ALPHA); + renderer.RenderThingSet(General.Map.ThingsFilter.VisibleThings, 1.0f); + renderer.Finish(); + } + + // Render selection + if (renderer.StartOverlay(true)) + { + for (int i = 0; i < Linedef.NUM_ARGS; i++) BuilderPlug.Me.RenderAssociations(renderer, association[i]); + if ((highlighted != null) && !highlighted.IsDisposed) BuilderPlug.Me.RenderReverseAssociations(renderer, highlightasso); + if (selecting) RenderMultiSelection(); + renderer.Finish(); + } + + renderer.Present(); + } + + // Selection + protected override void OnSelectBegin() + { + // Item highlighted? + if ((highlighted != null) && !highlighted.IsDisposed) + { + // Flip selection + highlighted.Selected = !highlighted.Selected; + + // Update display + if (renderer.StartPlotter(false)) + { + // Redraw highlight to show selection + renderer.PlotLinedef(highlighted, renderer.DetermineLinedefColor(highlighted)); + renderer.PlotVertex(highlighted.Start, renderer.DetermineVertexColor(highlighted.Start)); + renderer.PlotVertex(highlighted.End, renderer.DetermineVertexColor(highlighted.End)); + renderer.Finish(); + renderer.Present(); + } + } + else + { + // Start rectangular selection + StartMultiSelection(); + } + + base.OnSelectBegin(); + } + + // End selection + protected override void OnSelectEnd() + { + // Not stopping from multiselection? + if (!selecting) + { + // Item highlighted? + if ((highlighted != null) && !highlighted.IsDisposed) + { + // Update display + if (renderer.StartPlotter(false)) + { + // Render highlighted item + renderer.PlotLinedef(highlighted, General.Colors.Highlight); + renderer.PlotVertex(highlighted.Start, renderer.DetermineVertexColor(highlighted.Start)); + renderer.PlotVertex(highlighted.End, renderer.DetermineVertexColor(highlighted.End)); + renderer.Finish(); + renderer.Present(); + } + } + } + + base.OnSelectEnd(); + } + + // Start editing + protected override void OnEditBegin() + { + // Item highlighted? + if ((highlighted != null) && !highlighted.IsDisposed) + { + // Edit pressed in this mode + editpressed = true; + + // Highlighted item not selected? + if (!highlighted.Selected && (BuilderPlug.Me.AutoClearSelection || (General.Map.Map.SelectedLinedefsCount == 0))) + { + // Make this the only selection + General.Map.Map.ClearSelectedLinedefs(); + highlighted.Selected = true; + General.Interface.RedrawDisplay(); + } + + // Update display + if (renderer.StartPlotter(false)) + { + // Redraw highlight to show selection + renderer.PlotLinedef(highlighted, renderer.DetermineLinedefColor(highlighted)); + renderer.PlotVertex(highlighted.Start, renderer.DetermineVertexColor(highlighted.Start)); + renderer.PlotVertex(highlighted.End, renderer.DetermineVertexColor(highlighted.End)); + renderer.Finish(); + renderer.Present(); + } + } + else + { + // Start drawing mode + DrawGeometryMode drawmode = new DrawGeometryMode(); + bool snaptogrid = General.Interface.ShiftState ^ General.Interface.SnapToGrid; + bool snaptonearest = General.Interface.CtrlState ^ General.Interface.AutoMerge; + DrawnVertex v = DrawGeometryMode.GetCurrentPosition(mousemappos, snaptonearest, snaptogrid, renderer, new List()); + + if (drawmode.DrawPointAt(v)) + General.Editing.ChangeMode(drawmode); + else + General.Interface.DisplayStatus(StatusType.Warning, "Failed to draw point: outside of map boundaries."); + } + + base.OnEditBegin(); + } + + // Done editing + protected override void OnEditEnd() + { + // Edit pressed in this mode? + if (editpressed) + { + // Anything selected? + ICollection selected = General.Map.Map.GetSelectedLinedefs(true); + if (selected.Count > 0) + { + if (General.Interface.IsActiveWindow) + { + // Show line edit dialog + General.Interface.ShowEditLinedefs(selected); + General.Map.Map.Update(); + + // When a single line was selected, deselect it now + if (selected.Count == 1) General.Map.Map.ClearSelectedLinedefs(); + + // Update entire display + General.Interface.RedrawDisplay(); + } + } + } + + editpressed = false; + base.OnEditEnd(); + } + + // Mouse moves + public override void OnMouseMove(MouseEventArgs e) + { + base.OnMouseMove(e); + + // Not holding any buttons? + if (e.Button == MouseButtons.None) + { + // Find the nearest linedef within highlight range + Linedef l = General.Map.Map.NearestLinedefRange(mousemappos, BuilderPlug.Me.HighlightRange / renderer.Scale); + + // Highlight if not the same + if (l != highlighted) Highlight(l); + } + } + + // Mouse leaves + public override void OnMouseLeave(EventArgs e) + { + base.OnMouseLeave(e); + + // Highlight nothing + Highlight(null); + } + + // Mouse wants to drag + protected override void OnDragStart(MouseEventArgs e) + { + base.OnDragStart(e); + + // Edit button used? + if (General.Actions.CheckActionActive(null, "classicedit")) + { + // Anything highlighted? + if ((highlighted != null) && !highlighted.IsDisposed) + { + // Highlighted item not selected? + if (!highlighted.Selected) + { + // Select only this linedef for dragging + General.Map.Map.ClearSelectedLinedefs(); + highlighted.Selected = true; + } + + // Start dragging the selection + General.Editing.ChangeMode(new DragLinedefsMode(mousedownmappos)); + } + } + } + + // This is called wheh selection ends + protected override void OnEndMultiSelection() + { + bool selectionvolume = ((Math.Abs(base.selectionrect.Width) > 0.1f) && (Math.Abs(base.selectionrect.Height) > 0.1f)); + + if (BuilderPlug.Me.AutoClearSelection && !selectionvolume) + General.Map.Map.ClearSelectedLinedefs(); + + if (selectionvolume) + { + if (General.Interface.ShiftState ^ BuilderPlug.Me.AdditiveSelect) + { + // Go for all lines + foreach (Linedef l in General.Map.Map.Linedefs) + { + l.Selected |= ((l.Start.Position.x >= selectionrect.Left) && + (l.Start.Position.y >= selectionrect.Top) && + (l.Start.Position.x <= selectionrect.Right) && + (l.Start.Position.y <= selectionrect.Bottom) && + (l.End.Position.x >= selectionrect.Left) && + (l.End.Position.y >= selectionrect.Top) && + (l.End.Position.x <= selectionrect.Right) && + (l.End.Position.y <= selectionrect.Bottom)); + } + } + else + { + // Go for all lines + foreach (Linedef l in General.Map.Map.Linedefs) + { + l.Selected = ((l.Start.Position.x >= selectionrect.Left) && + (l.Start.Position.y >= selectionrect.Top) && + (l.Start.Position.x <= selectionrect.Right) && + (l.Start.Position.y <= selectionrect.Bottom) && + (l.End.Position.x >= selectionrect.Left) && + (l.End.Position.y >= selectionrect.Top) && + (l.End.Position.x <= selectionrect.Right) && + (l.End.Position.y <= selectionrect.Bottom)); + } + } + } + + base.OnEndMultiSelection(); + + // Clear overlay + if (renderer.StartOverlay(true)) renderer.Finish(); + + // Redraw + General.Interface.RedrawDisplay(); + } + + // This is called when the selection is updated + protected override void OnUpdateMultiSelection() + { + base.OnUpdateMultiSelection(); + + // Render selection + if (renderer.StartOverlay(true)) + { + RenderMultiSelection(); + renderer.Finish(); + renderer.Present(); + } + } + + // When copying + public override bool OnCopyBegin() + { + // No selection made? But we have a highlight! + if ((General.Map.Map.GetSelectedLinedefs(true).Count == 0) && (highlighted != null)) + { + // Make the highlight the selection + highlighted.Selected = true; + } + + return base.OnCopyBegin(); + } + + #endregion + + #region ================== Actions + + // This copies the properties + [BeginAction("classiccopyproperties")] + public void CopyProperties() + { + // Determine source linedefs + ICollection sel = null; + if (General.Map.Map.SelectedLinedefsCount > 0) + sel = General.Map.Map.GetSelectedLinedefs(true); + else if (highlighted != null) + { + sel = new List(); + sel.Add(highlighted); + } + + if (sel != null) + { + // Copy properties from first source linedef + BuilderPlug.Me.CopiedLinedefProps = new LinedefProperties(General.GetByIndex(sel, 0)); + General.Interface.DisplayStatus(StatusType.Action, "Copied linedef properties."); + } + } + + // This pastes the properties + [BeginAction("classicpasteproperties")] + public void PasteProperties() + { + if (BuilderPlug.Me.CopiedLinedefProps != null) + { + // Determine target linedefs + ICollection sel = null; + if (General.Map.Map.SelectedLinedefsCount > 0) + sel = General.Map.Map.GetSelectedLinedefs(true); + else if (highlighted != null) + { + sel = new List(); + sel.Add(highlighted); + } + + if (sel != null) + { + // Apply properties to selection + General.Map.UndoRedo.CreateUndo("Paste linedef properties"); + foreach (Linedef l in sel) + { + BuilderPlug.Me.CopiedLinedefProps.Apply(l); + l.UpdateCache(); + } + General.Interface.DisplayStatus(StatusType.Action, "Pasted linedef properties."); + + // Update and redraw + General.Map.IsChanged = true; + General.Interface.RefreshInfo(); + General.Interface.RedrawDisplay(); + } + } + } + + // This keeps only the single-sided lines selected + [BeginAction("selectsinglesided")] + public void SelectSingleSided() + { + int counter = 0; + ICollection selected = General.Map.Map.GetSelectedLinedefs(true); + foreach (Linedef ld in selected) + { + if ((ld.Front != null) && (ld.Back != null)) + ld.Selected = false; + else + counter++; + } + + General.Interface.DisplayStatus(StatusType.Action, "Selected only single-sided linedefs (" + counter + ")"); + General.Interface.RedrawDisplay(); + } + + // This keeps only the double-sided lines selected + [BeginAction("selectdoublesided")] + public void SelectDoubleSided() + { + int counter = 0; + ICollection selected = General.Map.Map.GetSelectedLinedefs(true); + foreach (Linedef ld in selected) + { + if ((ld.Front == null) || (ld.Back == null)) + ld.Selected = false; + else + counter++; + } + + General.Interface.DisplayStatus(StatusType.Action, "Selected only double-sided linedefs (" + counter + ")"); + General.Interface.RedrawDisplay(); + } + + // This clears the selection + [BeginAction("clearselection", BaseAction = true)] + public void ClearSelection() + { + // Clear selection + General.Map.Map.ClearAllSelected(); + + // Redraw + General.Interface.RedrawDisplay(); + } + + // This creates a new vertex at the mouse position + [BeginAction("insertitem", BaseAction = true)] + public virtual void InsertVertexAction() + { + // Start drawing mode + DrawGeometryMode drawmode = new DrawGeometryMode(); + if (mouseinside) + { + bool snaptogrid = General.Interface.ShiftState ^ General.Interface.SnapToGrid; + bool snaptonearest = General.Interface.CtrlState ^ General.Interface.AutoMerge; + DrawnVertex v = DrawGeometryMode.GetCurrentPosition(mousemappos, snaptonearest, snaptogrid, renderer, new List()); + drawmode.DrawPointAt(v); + } + General.Editing.ChangeMode(drawmode); + } + + [BeginAction("deleteitem", BaseAction = true)] + public void DeleteItem() + { + // Make list of selected linedefs + ICollection selected = General.Map.Map.GetSelectedLinedefs(true); + if ((selected.Count == 0) && (highlighted != null) && !highlighted.IsDisposed) selected.Add(highlighted); + + // Anything to do? + if (selected.Count > 0) + { + // Make undo + if (selected.Count > 1) + { + General.Map.UndoRedo.CreateUndo("Delete " + selected.Count + " linedefs"); + General.Interface.DisplayStatus(StatusType.Action, "Deleted " + selected.Count + " linedefs."); + } + else + { + General.Map.UndoRedo.CreateUndo("Delete linedef"); + General.Interface.DisplayStatus(StatusType.Action, "Deleted a linedef."); + } + + // Dispose selected linedefs + foreach (Linedef ld in selected) ld.Dispose(); + + // Update cache values + General.Map.IsChanged = true; + General.Map.Map.Update(); + + // Invoke a new mousemove so that the highlighted item updates + MouseEventArgs e = new MouseEventArgs(MouseButtons.None, 0, (int)mousepos.x, (int)mousepos.y, 0); + OnMouseMove(e); + + // Redraw screen + General.Interface.RedrawDisplay(); + } + } + + [BeginAction("splitlinedefs")] + public void SplitLinedefs() + { + // Make list of selected linedefs + ICollection selected = General.Map.Map.GetSelectedLinedefs(true); + if ((selected.Count == 0) && (highlighted != null) && !highlighted.IsDisposed) selected.Add(highlighted); + + // Anything to do? + if (selected.Count > 0) + { + // Make undo + if (selected.Count > 1) + { + General.Map.UndoRedo.CreateUndo("Split " + selected.Count + " linedefs"); + General.Interface.DisplayStatus(StatusType.Action, "Split " + selected.Count + " linedefs."); + } + else + { + General.Map.UndoRedo.CreateUndo("Split linedef"); + General.Interface.DisplayStatus(StatusType.Action, "Split a linedef."); + } + + // Go for all linedefs to split + foreach (Linedef ld in selected) + { + Vertex splitvertex; + + // Linedef highlighted? + if (ld == highlighted) + { + // Split at nearest position on the line + Vector2D nearestpos = ld.NearestOnLine(mousemappos); + splitvertex = General.Map.Map.CreateVertex(nearestpos); + } + else + { + // Split in middle of line + splitvertex = General.Map.Map.CreateVertex(ld.GetCenterPoint()); + } + + if (splitvertex == null) + { + General.Map.UndoRedo.WithdrawUndo(); + break; + } + + // Snap to map format accuracy + splitvertex.SnapToAccuracy(); + + // Split the line + Linedef sld = ld.Split(splitvertex); + if (sld == null) + { + General.Map.UndoRedo.WithdrawUndo(); + break; + } + } + + // Update cache values + General.Map.IsChanged = true; + General.Map.Map.Update(); + + // Redraw screen + General.Interface.RedrawDisplay(); + } + } + + [BeginAction("curvelinesmode")] + public void CurveLinedefs() + { + // No selected lines? + ICollection selected = General.Map.Map.GetSelectedLinedefs(true); + if (selected.Count == 0) + { + // Anything highlighted? + if (highlighted != null) + { + // Select the highlighted item + highlighted.Selected = true; + selected.Add(highlighted); + } + } + + // Any selected lines? + if (selected.Count > 0) + { + // Go into curve linedefs mode + General.Editing.ChangeMode(new CurveLinedefsMode(new LinedefsMode())); + } + } + + [BeginAction("fliplinedefs")] + public void FlipLinedefs() + { + // No selected lines? + ICollection selected = General.Map.Map.GetSelectedLinedefs(true); + if (selected.Count == 0) + { + // Anything highlighted? + if (highlighted != null) + { + // Select the highlighted item + highlighted.Selected = true; + selected.Add(highlighted); + } + } + + // Any selected lines? + if (selected.Count > 0) + { + // Make undo + if (selected.Count > 1) + { + General.Map.UndoRedo.CreateUndo("Flip " + selected.Count + " linedefs"); + General.Interface.DisplayStatus(StatusType.Action, "Flipped " + selected.Count + " linedefs."); + } + else + { + General.Map.UndoRedo.CreateUndo("Flip linedef"); + General.Interface.DisplayStatus(StatusType.Action, "Flipped a linedef."); + } + + // Flip all selected linedefs + foreach (Linedef l in selected) + { + l.FlipVertices(); + l.FlipSidedefs(); + } + + // Remove selection if only one was selected + if (selected.Count == 1) + { + foreach (Linedef ld in selected) ld.Selected = false; + selected.Clear(); + } + + // Redraw + General.Map.Map.Update(); + General.Map.IsChanged = true; + General.Interface.RefreshInfo(); + General.Interface.RedrawDisplay(); + } + } + + [BeginAction("flipsidedefs")] + public void FlipSidedefs() + { + // No selected lines? + ICollection selected = General.Map.Map.GetSelectedLinedefs(true); + if (selected.Count == 0) + { + // Anything highlighted? + if (highlighted != null) + { + // Select the highlighted item + highlighted.Selected = true; + selected.Add(highlighted); + } + } + + // Any selected lines? + if (selected.Count > 0) + { + // Make undo + if (selected.Count > 1) + { + General.Map.UndoRedo.CreateUndo("Flip " + selected.Count + " sidedefs"); + General.Interface.DisplayStatus(StatusType.Action, "Flipped " + selected.Count + " sidedefs."); + } + else + { + General.Map.UndoRedo.CreateUndo("Flip sidedefs"); + General.Interface.DisplayStatus(StatusType.Action, "Flipped sidedefs."); + } + + // Flip sidedefs in all selected linedefs + foreach (Linedef l in selected) + { + l.FlipSidedefs(); + if (l.Front != null) l.Front.Sector.UpdateNeeded = true; + if (l.Back != null) l.Back.Sector.UpdateNeeded = true; + } + + // Remove selection if only one was selected + if (selected.Count == 1) + { + foreach (Linedef ld in selected) ld.Selected = false; + selected.Clear(); + } + + // Redraw + General.Map.Map.Update(); + General.Map.IsChanged = true; + General.Interface.RefreshInfo(); + General.Interface.RedrawDisplay(); + } + } + + #endregion + } +} diff --git a/Source/Plugins/BuilderModes/ClassicModes/MakeSectorMode.cs b/Source/Plugins/BuilderModes/ClassicModes/MakeSectorMode.cs new file mode 100644 index 0000000..eff5263 --- /dev/null +++ b/Source/Plugins/BuilderModes/ClassicModes/MakeSectorMode.cs @@ -0,0 +1,544 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using CodeImp.DoomBuilder.Windows; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Geometry; +using CodeImp.DoomBuilder.Editing; +using System.Drawing; +using CodeImp.DoomBuilder.Actions; + +#endregion + +namespace CodeImp.DoomBuilder.BuilderModes +{ + [EditMode(DisplayName = "Make Sectors Mode", + SwitchAction = "makesectormode", + ButtonImage = "NewSector2.png", // Image resource name for the button + ButtonOrder = int.MinValue + 202, // Position of the button (lower is more to the left) + ButtonGroup = "000_editing", + UseByDefault = true)] + + public class MakeSectorMode : BaseClassicMode + { + #region ================== Constants + + private const double FLASH_DURATION = 300.0f; + + #endregion + + #region ================== Variables + + // Nearest sidedef + private LinedefSide editside; + private LinedefSide nearestside; + private List allsides; + private List alllines; + + // Flash polygon + private FlatVertex[] flashpolygon; + private float flashintensity; + private double flashstarttime; + + // Interface + protected bool selectpressed; + protected bool editpressed; + + #endregion + + #region ================== Properties + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + public MakeSectorMode() + { + } + + // Disposer + public override void Dispose() + { + // Not already disposed? + if (!isdisposed) + { + // Clean up + nearestside = null; + allsides = null; + + // Dispose base + base.Dispose(); + } + } + + #endregion + + #region ================== Methods + + // This draws the geometry + private void DrawGeometry() + { + Dictionary associates = new Dictionary(); + + // Render lines and vertices + if (renderer.StartPlotter(true)) + { + renderer.PlotLinedefSet(General.Map.Map.Linedefs); + + // Render sector indication + if (allsides != null) + { + foreach (LinedefSide sd in allsides) + { + if (sd.Front) + { + if (sd.Line.Front != null) + { + if (!associates.ContainsKey(sd.Line.Front.Sector)) + { + renderer.PlotSector(sd.Line.Front.Sector, General.Colors.Indication); + associates[sd.Line.Front.Sector] = sd.Line.Front.Sector; + } + } + } + else + { + if (sd.Line.Back != null) + { + if (!associates.ContainsKey(sd.Line.Back.Sector)) + { + renderer.PlotSector(sd.Line.Back.Sector, General.Colors.Indication); + associates[sd.Line.Back.Sector] = sd.Line.Back.Sector; + } + } + } + } + } + + // Render highlight + if (alllines != null) + { + foreach (Linedef l in alllines) renderer.PlotLinedef(l, General.Colors.Highlight); + } + + renderer.PlotVerticesSet(General.Map.Map.Vertices); + renderer.Finish(); + } + } + + // This draws the overlay + private void DrawOverlay() + { + // Redraw overlay + if (renderer.StartOverlay(true)) + { + if ((flashpolygon != null) && (flashintensity > 0.0f)) + { + renderer.RenderGeometry(flashpolygon, null, true); + } + + renderer.Finish(); + } + } + + // This highlights a new region + protected void Highlight(bool buttonspressed) + { + LinedefSide newnearest; + + // Mouse inside? + if (mouseinside) + { + // Highlighting from a new sidedef? + Linedef nl = General.Map.Map.NearestLinedef(mousemappos); + if (nl != null) + { + float side = nl.SideOfLine(mousemappos); + newnearest = new LinedefSide(nl, (side <= 0.0f)); + if (newnearest != nearestside) + { + // Only change when buttons are not pressed + if (!buttonspressed || (editside == newnearest)) + { + // Find new sector + General.Interface.SetCursor(Cursors.AppStarting); + nearestside = newnearest; + allsides = Tools.FindPotentialSectorAt(mousemappos); + if (allsides != null) + { + alllines = new List(allsides.Count); + foreach (LinedefSide sd in allsides) alllines.Add(sd.Line); + } + else + { + alllines = null; + } + General.Interface.SetCursor(Cursors.Default); + } + else + { + // Don't highlight this one + nearestside = null; + allsides = null; + alllines = null; + } + + // Redraw overlay + DrawGeometry(); + renderer.Present(); + } + } + } + else + { + // No valid region + nearestside = null; + allsides = null; + alllines = null; + + // Redraw overlay + DrawGeometry(); + renderer.Present(); + } + } + + // This makes this highlighted potential sector + private Sector MakeSector() + { + General.Interface.SetCursor(Cursors.WaitCursor); + General.Settings.FindDefaultDrawSettings(); + General.Map.UndoRedo.CreateUndo("Make Sector"); + + // Mark the lines we are going to use for this sector + General.Map.Map.ClearAllMarks(true); + foreach (LinedefSide ls in allsides) ls.Line.Marked = false; + List oldlines = General.Map.Map.GetMarkedLinedefs(true); + + // Make the sector + Sector s = Tools.MakeSector(allsides, oldlines); + if (s != null) + { + // Now we go for all the lines along the sector to + // see if they only have a back side. In that case we want + // to flip the linedef to that it only has a front side. + foreach (Sidedef sd in s.Sidedefs) + { + if ((sd.Line.Front == null) && (sd.Line.Back != null)) + { + // Flip linedef + sd.Line.FlipVertices(); + sd.Line.FlipSidedefs(); + } + } + + General.Map.Data.UpdateUsedTextures(); + General.Interface.SetCursor(Cursors.Default); + return s; + } + else + { + General.Map.UndoRedo.WithdrawUndo(); + return null; + } + } + + #endregion + + #region ================== Events + + public override void OnHelp() + { + General.ShowHelp("e_makesectors.html"); + } + + // When the mapset changes (undo/redo) + public override void OnMapSetChangeBegin() + { + // No valid region + nearestside = null; + allsides = null; + alllines = null; + + base.OnMapSetChangeBegin(); + } + + // Cancel mode + public override void OnCancel() + { + base.OnCancel(); + + // Return to base mode + General.Editing.ChangeMode(new SectorsMode()); + } + + // Mode engages + public override void OnEngage() + { + base.OnEngage(); + + // Make customized presentation + CustomPresentation p = new CustomPresentation(); + p.AddLayer(new PresentLayer(RendererLayer.Background, BlendingMode.Mask, General.Settings.BackgroundAlpha)); + p.AddLayer(new PresentLayer(RendererLayer.Surface, BlendingMode.Mask)); + p.AddLayer(new PresentLayer(RendererLayer.Grid, BlendingMode.Mask)); + p.AddLayer(new PresentLayer(RendererLayer.Overlay, BlendingMode.Alpha, 1f, true)); + p.AddLayer(new PresentLayer(RendererLayer.Things, BlendingMode.Alpha, Presentation.THINGS_BACK_ALPHA, false)); + p.AddLayer(new PresentLayer(RendererLayer.Geometry, BlendingMode.Alpha, 1f, true)); + renderer.SetPresentation(p); + General.Map.Map.SelectionType = SelectionType.All; + } + + // Mode disengages + public override void OnDisengage() + { + base.OnDisengage(); + + // Check which mode we are switching to + if (General.Editing.NewMode is VerticesMode) + { + // Convert selection to vertices + + // Clear selected sectors + General.Map.Map.ClearSelectedSectors(); + } + else if (General.Editing.NewMode is LinedefsMode) + { + // Convert selection to linedefs + + // Clear selected sectors + General.Map.Map.ClearSelectedSectors(); + } + + // Hide highlight info + General.Interface.HideInfo(); + + // Stop processing + General.Interface.DisableProcessing(); + } + + // This redraws the display + public override void OnRedrawDisplay() + { + renderer.RedrawSurface(); + + // Render lines and vertices + DrawGeometry(); + + // Render things + if (renderer.StartThings(true)) + { + renderer.RenderThingSet(General.Map.ThingsFilter.HiddenThings, Presentation.THINGS_HIDDEN_ALPHA); + renderer.RenderThingSet(General.Map.ThingsFilter.VisibleThings, 1.0f); + renderer.Finish(); + } + + // Render overlay + DrawOverlay(); + renderer.Present(); + } + + // Start select + protected override void OnSelectBegin() + { + // Select pressed in this mode + selectpressed = true; + editside = nearestside; + base.OnEditBegin(); + } + + // Done selecting + protected override void OnSelectEnd() + { + // Select was pressed in this mode? + if (selectpressed && (editside == nearestside) && (nearestside != null)) + { + // Possible to make a sector? + if (allsides != null) + { + // Make the sector + Sector s = MakeSector(); + if (s != null) + { + // Quickly flash this sector to indicate it was created + General.Map.IsChanged = true; + General.Map.Map.Update(); + General.Interface.RedrawDisplay(); + flashpolygon = new FlatVertex[s.FlatVertices.Length]; + s.FlatVertices.CopyTo(flashpolygon, 0); + flashintensity = 1.0f; + flashstarttime = General.stopwatch.Elapsed.TotalMilliseconds; + General.Interface.EnableProcessing(); + } + + // Redraw overlay + DrawGeometry(); + DrawOverlay(); + renderer.Present(); + } + } + + selectpressed = false; + base.OnSelectEnd(); + } + + // Start editing + protected override void OnEditBegin() + { + // Edit pressed in this mode + editpressed = true; + editside = nearestside; + base.OnEditBegin(); + } + + // Done editing + protected override void OnEditEnd() + { + // Edit was pressed in this mode? + if (editpressed && (editside == nearestside) && (nearestside != null)) + { + // Possible to make a sector? + if (allsides != null) + { + // Make the sector + Sector s = MakeSector(); + if (s != null) + { + General.Map.Map.Update(); + + // Edit the sector + List secs = new List(); secs.Add(s); + if (General.Interface.ShowEditSectors(secs) == DialogResult.OK) + { + // Quickly flash this sector to indicate it was created + General.Map.IsChanged = true; + General.Map.Map.Update(); + flashpolygon = new FlatVertex[s.FlatVertices.Length]; + s.FlatVertices.CopyTo(flashpolygon, 0); + flashintensity = 1.0f; + flashstarttime = General.stopwatch.Elapsed.TotalMilliseconds; + General.Interface.EnableProcessing(); + } + else + { + // Undo + General.Map.UndoRedo.WithdrawUndo(); + } + } + + // Redraw overlay + DrawGeometry(); + DrawOverlay(); + renderer.Present(); + } + } + + editpressed = false; + base.OnEditEnd(); + } + + // Mouse moves + public override void OnMouseMove(MouseEventArgs e) + { + base.OnMouseMove(e); + + // Highlight the region + Highlight((e.Button != MouseButtons.None)); + } + + // Mouse leaves + public override void OnMouseLeave(EventArgs e) + { + base.OnMouseLeave(e); + + // Highlight nothing + Highlight(false); + } + + // Something is undone + public override void OnUndoEnd() + { + base.OnUndoEnd(); + + editside = null; + nearestside = null; + alllines.Clear(); + allsides.Clear(); + } + + // Something is redone + public override void OnRedoEnd() + { + base.OnRedoEnd(); + + editside = null; + nearestside = null; + alllines.Clear(); + allsides.Clear(); + } + + // Processing + public override void OnProcess(double deltatime) + { + base.OnProcess(deltatime); + + // Process flash + if (flashpolygon != null) + { + // Determine the intensity of the flash by time elapsed + double curtime = General.stopwatch.Elapsed.TotalMilliseconds; + flashintensity = 1f - (float)((curtime - flashstarttime) / FLASH_DURATION); + if (flashintensity > 0.0f) + { + // Update vertices in polygon + PixelColor pc = new PixelColor((byte)(flashintensity * 255.0f), 255, 255, 255); + int intcolor = pc.ToInt(); + for (int i = 0; i < flashpolygon.Length; i++) flashpolygon[i].c = intcolor; + } + else + { + // End of flash, trash the polygon + flashpolygon = null; + flashintensity = 0.0f; + General.Interface.DisableProcessing(); + } + + // Redraw overlay + DrawOverlay(); + renderer.Present(); + } + } + + #endregion + + #region ================== Actions + + #endregion + } +} + diff --git a/Source/Plugins/BuilderModes/ClassicModes/SectorsMode.cs b/Source/Plugins/BuilderModes/ClassicModes/SectorsMode.cs new file mode 100644 index 0000000..34caf4c --- /dev/null +++ b/Source/Plugins/BuilderModes/ClassicModes/SectorsMode.cs @@ -0,0 +1,1438 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using CodeImp.DoomBuilder.Windows; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Geometry; +using CodeImp.DoomBuilder.Editing; +using System.Drawing; +using CodeImp.DoomBuilder.Actions; +using CodeImp.DoomBuilder.Types; +using CodeImp.DoomBuilder.BuilderModes.Interface; + +#endregion + +namespace CodeImp.DoomBuilder.BuilderModes +{ + [EditMode(DisplayName = "Sectors Mode", + SwitchAction = "sectorsmode", // Action name used to switch to this mode + ButtonImage = "SectorsMode.png", // Image resource name for the button + ButtonOrder = int.MinValue + 200, // Position of the button (lower is more to the left) + ButtonGroup = "000_editing", + UseByDefault = true, + SafeStartMode = true)] + + public class SectorsMode : BaseClassicMode + { + #region ================== Constants + + #endregion + + #region ================== Variables + + // Highlighted item + protected Sector highlighted; + private Association highlightasso = new Association(); + + // Interface + protected bool editpressed; + + // Labels + private Dictionary labels; + + #endregion + + #region ================== Properties + + public override object HighlightedObject { get { return highlighted; } } + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + public SectorsMode() + { + } + + // Disposer + public override void Dispose() + { + // Not already disposed? + if (!isdisposed) + { + // Dispose old labels + foreach (KeyValuePair lbl in labels) + foreach (TextLabel l in lbl.Value) l.Dispose(); + + // Dispose base + base.Dispose(); + } + } + + #endregion + + #region ================== Methods + + // This makes a CRC for the selection + public int CreateSelectionCRC() + { + CRC crc = new CRC(); + ICollection orderedselection = General.Map.Map.GetSelectedSectors(true); + crc.Add(orderedselection.Count); + foreach (Sector s in orderedselection) + { + crc.Add(s.FixedIndex); + } + return (int)(crc.Value & 0xFFFFFFFF); + } + + // This sets up new labels + private void SetupLabels() + { + if (labels != null) + { + // Dispose old labels + foreach (KeyValuePair lbl in labels) + foreach (TextLabel l in lbl.Value) l.Dispose(); + } + + // Make text labels for sectors + labels = new Dictionary(General.Map.Map.Sectors.Count); + foreach (Sector s in General.Map.Map.Sectors) + { + // Setup labels + TextLabel[] labelarray = new TextLabel[s.Labels.Count]; + for (int i = 0; i < s.Labels.Count; i++) + { + Vector2D v = s.Labels[i].position; + labelarray[i] = new TextLabel(20); + labelarray[i].TransformCoords = true; + labelarray[i].Rectangle = new RectangleF(v.x, v.y, 0.0f, 0.0f); + labelarray[i].AlignX = TextAlignmentX.Center; + labelarray[i].AlignY = TextAlignmentY.Middle; + labelarray[i].Scale = 14f; + labelarray[i].Color = General.Colors.Highlight.WithAlpha(255); + labelarray[i].Backcolor = General.Colors.Background.WithAlpha(255); + } + labels.Add(s, labelarray); + } + } + + // This updates the overlay + private void UpdateOverlay() + { + if (renderer.StartOverlay(true)) + { + if (BuilderPlug.Me.ViewSelectionNumbers) + { + // Go for all selected sectors + ICollection orderedselection = General.Map.Map.GetSelectedSectors(true); + foreach (Sector s in orderedselection) + { + // Render labels + TextLabel[] labelarray = labels[s]; + for (int i = 0; i < s.Labels.Count; i++) + { + TextLabel l = labelarray[i]; + + // Render only when enough space for the label to see + float requiredsize = (l.TextSize.Height / 2) / renderer.Scale; + if (requiredsize < s.Labels[i].radius) renderer.RenderText(l); + } + } + } + + renderer.Finish(); + } + } + + // Support function for joining and merging sectors + private void JoinMergeSectors(bool removelines) + { + // Remove lines in betwen joining sectors? + if (removelines) + { + // Go for all selected linedefs + List selectedlines = new List(General.Map.Map.GetSelectedLinedefs(true)); + foreach (Linedef ld in selectedlines) + { + // Front and back side? + if ((ld.Front != null) && (ld.Back != null)) + { + // Both a selected sector, but not the same? + if (ld.Front.Sector.Selected && ld.Back.Sector.Selected && + (ld.Front.Sector != ld.Back.Sector)) + { + // Remove this line + ld.Dispose(); + } + } + } + } + + // Find the first sector that is not disposed + List orderedselection = new List(General.Map.Map.GetSelectedSectors(true)); + Sector first = null; + foreach (Sector s in orderedselection) + if (!s.IsDisposed) { first = s; break; } + + // Join all selected sectors with the first + for (int i = 0; i < orderedselection.Count; i++) + if ((orderedselection[i] != first) && !orderedselection[i].IsDisposed) + orderedselection[i].Join(first); + + // Clear selection + General.Map.Map.ClearAllSelected(); + + // Update + General.Map.Map.Update(); + + // Make text labels for sectors + SetupLabels(); + UpdateSelectedLabels(); + } + + // This highlights a new item + protected void Highlight(Sector s) + { + bool completeredraw = false; + + // Often we can get away by simply undrawing the previous + // highlight and drawing the new highlight. But if associations + // are or were drawn we need to redraw the entire display. + + // Previous association highlights something? + if ((highlighted != null) && (highlighted.Tag > 0)) completeredraw = true; + + // Set highlight association + if (s != null) + highlightasso.Set(s.Tag, UniversalType.SectorTag); + else + highlightasso.Set(0, 0); + + // New association highlights something? + if ((s != null) && (s.Tag > 0)) completeredraw = true; + + // Change label color + if ((highlighted != null) && !highlighted.IsDisposed) + { + TextLabel[] labelarray = labels[highlighted]; + foreach (TextLabel l in labelarray) l.Color = General.Colors.Selection; + } + + // Change label color + if ((s != null) && !s.IsDisposed) + { + TextLabel[] labelarray = labels[s]; + foreach (TextLabel l in labelarray) l.Color = General.Colors.Highlight; + } + + // If we're changing associations, then we + // need to redraw the entire display + if (completeredraw) + { + // Set new highlight and redraw completely + highlighted = s; + General.Interface.RedrawDisplay(); + } + else + { + // Update display + if (renderer.StartPlotter(false)) + { + // Undraw previous highlight + if ((highlighted != null) && !highlighted.IsDisposed) + renderer.PlotSector(highlighted); + + /* + // Undraw highlighted things + if(highlighted != null) + foreach(Thing t in highlighted.Things) + renderer.RenderThing(t, renderer.DetermineThingColor(t)); + */ + + // Set new highlight + highlighted = s; + + // Render highlighted item + if ((highlighted != null) && !highlighted.IsDisposed) + renderer.PlotSector(highlighted, General.Colors.Highlight); + + /* + // Render highlighted things + if(highlighted != null) + foreach(Thing t in highlighted.Things) + renderer.RenderThing(t, General.Colors.Highlight); + */ + + // Done + renderer.Finish(); + } + + UpdateOverlay(); + renderer.Present(); + } + + // Show highlight info + if ((highlighted != null) && !highlighted.IsDisposed) + General.Interface.ShowSectorInfo(highlighted); + else + General.Interface.HideInfo(); + } + + // This selectes or deselects a sector + protected void SelectSector(Sector s, bool selectstate, bool update) + { + bool selectionchanged = false; + + if (!s.IsDisposed) + { + // Select the sector? + if (selectstate && !s.Selected) + { + s.Selected = true; + selectionchanged = true; + + // Setup labels + ICollection orderedselection = General.Map.Map.GetSelectedSectors(true); + TextLabel[] labelarray = labels[s]; + foreach (TextLabel l in labelarray) + { + l.Text = orderedselection.Count.ToString(); + l.Color = General.Colors.Selection; + } + } + // Deselect the sector? + else if (!selectstate && s.Selected) + { + s.Selected = false; + selectionchanged = true; + + // Clear labels + TextLabel[] labelarray = labels[s]; + foreach (TextLabel l in labelarray) l.Text = ""; + + // Update all other labels + UpdateSelectedLabels(); + } + + // Selection changed? + if (selectionchanged) + { + // Make update lines selection + foreach (Sidedef sd in s.Sidedefs) + { + bool front, back; + if (sd.Line.Front != null) front = sd.Line.Front.Sector.Selected; else front = false; + if (sd.Line.Back != null) back = sd.Line.Back.Sector.Selected; else back = false; + sd.Line.Selected = front | back; + } + } + + if (update) + { + UpdateOverlay(); + renderer.Present(); + } + } + } + + // This updates labels from the selected sectors + private void UpdateSelectedLabels() + { + // Go for all labels in all selected sectors + ICollection orderedselection = General.Map.Map.GetSelectedSectors(true); + int index = 0; + foreach (Sector s in orderedselection) + { + TextLabel[] labelarray = labels[s]; + foreach (TextLabel l in labelarray) + { + // Make sure the text and color are right + int labelnum = index + 1; + l.Text = labelnum.ToString(); + l.Color = General.Colors.Selection; + } + index++; + } + } + + #endregion + + #region ================== Events + + public override void OnHelp() + { + General.ShowHelp("e_sectors.html"); + } + + // Cancel mode + public override void OnCancel() + { + base.OnCancel(); + + // Return to this mode + General.Editing.ChangeMode(new SectorsMode()); + } + + // Mode engages + public override void OnEngage() + { + base.OnEngage(); + renderer.SetPresentation(Presentation.Standard); + + // Add toolbar buttons + General.Interface.AddButton(BuilderPlug.Me.MenusForm.CopyProperties); + General.Interface.AddButton(BuilderPlug.Me.MenusForm.PasteProperties); + General.Interface.AddButton(BuilderPlug.Me.MenusForm.SeparatorCopyPaste); + General.Interface.AddButton(BuilderPlug.Me.MenusForm.ViewSelectionNumbers); + General.Interface.AddButton(BuilderPlug.Me.MenusForm.SeparatorSectors1); + General.Interface.AddButton(BuilderPlug.Me.MenusForm.MakeGradientBrightness); + General.Interface.AddButton(BuilderPlug.Me.MenusForm.MakeGradientFloors); + General.Interface.AddButton(BuilderPlug.Me.MenusForm.MakeGradientCeilings); + + // Convert geometry selection to sectors only + General.Map.Map.ConvertSelection(SelectionType.Sectors); + + // Make text labels for sectors + SetupLabels(); + + // Update + UpdateSelectedLabels(); + UpdateOverlay(); + } + + // Mode disengages + public override void OnDisengage() + { + base.OnDisengage(); + + // Remove toolbar buttons + General.Interface.RemoveButton(BuilderPlug.Me.MenusForm.CopyProperties); + General.Interface.RemoveButton(BuilderPlug.Me.MenusForm.PasteProperties); + General.Interface.RemoveButton(BuilderPlug.Me.MenusForm.SeparatorCopyPaste); + General.Interface.RemoveButton(BuilderPlug.Me.MenusForm.ViewSelectionNumbers); + General.Interface.RemoveButton(BuilderPlug.Me.MenusForm.SeparatorSectors1); + General.Interface.RemoveButton(BuilderPlug.Me.MenusForm.MakeGradientBrightness); + General.Interface.RemoveButton(BuilderPlug.Me.MenusForm.MakeGradientFloors); + General.Interface.RemoveButton(BuilderPlug.Me.MenusForm.MakeGradientCeilings); + + // Keep only sectors selected + General.Map.Map.ClearSelectedLinedefs(); + + // Going to EditSelectionMode? + if (General.Editing.NewMode is EditSelectionMode) + { + // Not pasting anything? + EditSelectionMode editmode = (General.Editing.NewMode as EditSelectionMode); + if (!editmode.Pasting) + { + // No selection made? But we have a highlight! + if ((General.Map.Map.GetSelectedSectors(true).Count == 0) && (highlighted != null)) + { + // Make the highlight the selection + SelectSector(highlighted, true, false); + } + } + } + + // Hide highlight info + General.Interface.HideInfo(); + } + + // This redraws the display + public override void OnRedrawDisplay() + { + renderer.RedrawSurface(); + + // Render lines and vertices + if (renderer.StartPlotter(true)) + { + renderer.PlotLinedefSet(General.Map.Map.Linedefs); + renderer.PlotVerticesSet(General.Map.Map.Vertices); + if ((highlighted != null) && !highlighted.IsDisposed) + { + renderer.PlotSector(highlighted, General.Colors.Highlight); + BuilderPlug.Me.PlotReverseAssociations(renderer, highlightasso); + } + renderer.Finish(); + } + + // Render things + if (renderer.StartThings(true)) + { + renderer.RenderThingSet(General.Map.ThingsFilter.HiddenThings, Presentation.THINGS_HIDDEN_ALPHA); + renderer.RenderThingSet(General.Map.ThingsFilter.VisibleThings, 1.0f); + renderer.Finish(); + } + + // Render selection + if (renderer.StartOverlay(true)) + { + if ((highlighted != null) && !highlighted.IsDisposed) BuilderPlug.Me.RenderReverseAssociations(renderer, highlightasso); + if (selecting) RenderMultiSelection(); + renderer.Finish(); + } + + // Render overlay + UpdateOverlay(); + + renderer.Present(); + } + + // Selection + protected override void OnSelectBegin() + { + // Item highlighted? + if ((highlighted != null) && !highlighted.IsDisposed) + { + // Flip selection + SelectSector(highlighted, !highlighted.Selected, true); + + // Update display + if (renderer.StartPlotter(false)) + { + // Redraw highlight to show selection + renderer.PlotSector(highlighted); + renderer.Finish(); + renderer.Present(); + } + } + else + { + // Start making a selection + StartMultiSelection(); + } + + base.OnSelectBegin(); + } + + // End selection + protected override void OnSelectEnd() + { + // Not stopping from multiselection? + if (!selecting) + { + // Item highlighted? + if ((highlighted != null) && !highlighted.IsDisposed) + { + // Update display + if (renderer.StartPlotter(false)) + { + // Render highlighted item + renderer.PlotSector(highlighted, General.Colors.Highlight); + renderer.Finish(); + renderer.Present(); + } + + // Update overlay + TextLabel[] labelarray = labels[highlighted]; + foreach (TextLabel l in labelarray) l.Color = General.Colors.Highlight; + UpdateOverlay(); + renderer.Present(); + } + } + + base.OnSelectEnd(); + } + + // Start editing + protected override void OnEditBegin() + { + // Item highlighted? + if ((highlighted != null) && !highlighted.IsDisposed) + { + // Edit pressed in this mode + editpressed = true; + + // Highlighted item not selected? + if (!highlighted.Selected && (BuilderPlug.Me.AutoClearSelection || (General.Map.Map.SelectedSectorsCount == 0))) + { + // Make this the only selection + General.Map.Map.ClearSelectedSectors(); + General.Map.Map.ClearSelectedLinedefs(); + SelectSector(highlighted, true, false); + General.Interface.RedrawDisplay(); + } + + // Update display + if (renderer.StartPlotter(false)) + { + // Redraw highlight to show selection + renderer.PlotSector(highlighted); + renderer.Finish(); + renderer.Present(); + } + } + else + { + // Start drawing mode + DrawGeometryMode drawmode = new DrawGeometryMode(); + bool snaptogrid = General.Interface.ShiftState ^ General.Interface.SnapToGrid; + bool snaptonearest = General.Interface.CtrlState ^ General.Interface.AutoMerge; + DrawnVertex v = DrawGeometryMode.GetCurrentPosition(mousemappos, snaptonearest, snaptogrid, renderer, new List()); + + if (drawmode.DrawPointAt(v)) + General.Editing.ChangeMode(drawmode); + else + General.Interface.DisplayStatus(StatusType.Warning, "Failed to draw point: outside of map boundaries."); + } + + base.OnEditBegin(); + } + + // Done editing + protected override void OnEditEnd() + { + // Edit pressed in this mode? + if (editpressed) + { + // Anything selected? + ICollection selected = General.Map.Map.GetSelectedSectors(true); + if (selected.Count > 0) + { + if (General.Interface.IsActiveWindow) + { + // Show sector edit dialog + General.Interface.ShowEditSectors(selected); + General.Map.Map.Update(); + + // When a single sector was selected, deselect it now + if (selected.Count == 1) + { + General.Map.Map.ClearSelectedSectors(); + General.Map.Map.ClearSelectedLinedefs(); + } + + // Update entire display + General.Interface.RedrawDisplay(); + } + } + } + + editpressed = false; + base.OnEditEnd(); + } + + // Mouse moves + public override void OnMouseMove(MouseEventArgs e) + { + base.OnMouseMove(e); + + // Not holding any buttons? + if (e.Button == MouseButtons.None) + { + // Find the nearest linedef within highlight range + Linedef l = General.Map.Map.NearestLinedef(mousemappos); + if (l != null) + { + // Check on which side of the linedef the mouse is + float side = l.SideOfLine(mousemappos); + if (side > 0) + { + // Is there a sidedef here? + if (l.Back != null) + { + // Highlight if not the same + if (l.Back.Sector != highlighted) Highlight(l.Back.Sector); + } + else + { + // Highlight nothing + if (highlighted != null) Highlight(null); + } + } + else + { + // Is there a sidedef here? + if (l.Front != null) + { + // Highlight if not the same + if (l.Front.Sector != highlighted) Highlight(l.Front.Sector); + } + else + { + // Highlight nothing + if (highlighted != null) Highlight(null); + } + } + } + else + { + // Highlight nothing + if (highlighted != null) Highlight(null); + } + } + } + + // Mouse leaves + public override void OnMouseLeave(EventArgs e) + { + base.OnMouseLeave(e); + + // Highlight nothing + Highlight(null); + } + + // Mouse wants to drag + protected override void OnDragStart(MouseEventArgs e) + { + base.OnDragStart(e); + + // Edit button used? + if (General.Actions.CheckActionActive(null, "classicedit")) + { + // Anything highlighted? + if ((highlighted != null) && !highlighted.IsDisposed) + { + // Highlighted item not selected? + if (!highlighted.Selected) + { + // Select only this sector for dragging + General.Map.Map.ClearSelectedSectors(); + SelectSector(highlighted, true, true); + } + + // Start dragging the selection + General.Editing.ChangeMode(new DragSectorsMode(mousedownmappos)); + } + } + } + + // This is called wheh selection ends + protected override void OnEndMultiSelection() + { + bool selectionvolume = ((Math.Abs(base.selectionrect.Width) > 0.1f) && (Math.Abs(base.selectionrect.Height) > 0.1f)); + + if (BuilderPlug.Me.AutoClearSelection && !selectionvolume) + { + General.Map.Map.ClearSelectedLinedefs(); + General.Map.Map.ClearSelectedSectors(); + } + + if (selectionvolume) + { + if (General.Interface.ShiftState ^ BuilderPlug.Me.AdditiveSelect) + { + // Go for all lines + foreach (Linedef l in General.Map.Map.Linedefs) + { + l.Selected |= ((l.Start.Position.x >= selectionrect.Left) && + (l.Start.Position.y >= selectionrect.Top) && + (l.Start.Position.x <= selectionrect.Right) && + (l.Start.Position.y <= selectionrect.Bottom) && + (l.End.Position.x >= selectionrect.Left) && + (l.End.Position.y >= selectionrect.Top) && + (l.End.Position.x <= selectionrect.Right) && + (l.End.Position.y <= selectionrect.Bottom)); + } + } + else + { + // Go for all lines + foreach (Linedef l in General.Map.Map.Linedefs) + { + l.Selected = ((l.Start.Position.x >= selectionrect.Left) && + (l.Start.Position.y >= selectionrect.Top) && + (l.Start.Position.x <= selectionrect.Right) && + (l.Start.Position.y <= selectionrect.Bottom) && + (l.End.Position.x >= selectionrect.Left) && + (l.End.Position.y >= selectionrect.Top) && + (l.End.Position.x <= selectionrect.Right) && + (l.End.Position.y <= selectionrect.Bottom)); + } + } + + // Go for all sectors + foreach (Sector s in General.Map.Map.Sectors) + { + // Go for all sidedefs + bool allselected = true; + foreach (Sidedef sd in s.Sidedefs) + { + if (!sd.Line.Selected) + { + allselected = false; + break; + } + } + + // Sector completely selected? + SelectSector(s, allselected, false); + } + + // Make sure all linedefs reflect selected sectors + foreach (Sidedef sd in General.Map.Map.Sidedefs) + if (!sd.Sector.Selected && ((sd.Other == null) || !sd.Other.Sector.Selected)) + sd.Line.Selected = false; + } + + base.OnEndMultiSelection(); + if (renderer.StartOverlay(true)) renderer.Finish(); + General.Interface.RedrawDisplay(); + } + + // This is called when the selection is updated + protected override void OnUpdateMultiSelection() + { + base.OnUpdateMultiSelection(); + + // Render selection + if (renderer.StartOverlay(true)) + { + RenderMultiSelection(); + renderer.Finish(); + renderer.Present(); + } + } + + // When copying + public override bool OnCopyBegin() + { + // No selection made? But we have a highlight! + if ((General.Map.Map.GetSelectedSectors(true).Count == 0) && (highlighted != null)) + { + // Make the highlight the selection + SelectSector(highlighted, true, true); + } + + return base.OnCopyBegin(); + } + + // When undo is used + public override bool OnUndoBegin() + { + // Clear ordered selection + General.Map.Map.ClearAllSelected(); + + return base.OnUndoBegin(); + } + + // When undo is performed + public override void OnUndoEnd() + { + // Clear labels + SetupLabels(); + } + + // When redo is used + public override bool OnRedoBegin() + { + // Clear ordered selection + General.Map.Map.ClearAllSelected(); + + return base.OnRedoBegin(); + } + + // When redo is performed + public override void OnRedoEnd() + { + // Clear labels + SetupLabels(); + } + + #endregion + + #region ================== Actions + + // This copies the properties + [BeginAction("classiccopyproperties")] + public void CopyProperties() + { + // Determine source sectors + ICollection sel = null; + if (General.Map.Map.SelectedSectorsCount > 0) + sel = General.Map.Map.GetSelectedSectors(true); + else if (highlighted != null) + { + sel = new List(); + sel.Add(highlighted); + } + + if (sel != null) + { + // Copy properties from first source sectors + BuilderPlug.Me.CopiedSectorProps = new SectorProperties(General.GetByIndex(sel, 0)); + General.Interface.DisplayStatus(StatusType.Action, "Copied sector properties."); + } + } + + // This pastes the properties + [BeginAction("classicpasteproperties")] + public void PasteProperties() + { + if (BuilderPlug.Me.CopiedSectorProps != null) + { + // Determine target sectors + ICollection sel = null; + if (General.Map.Map.SelectedSectorsCount > 0) + sel = General.Map.Map.GetSelectedSectors(true); + else if (highlighted != null) + { + sel = new List(); + sel.Add(highlighted); + } + + if (sel != null) + { + // Apply properties to selection + General.Map.UndoRedo.CreateUndo("Paste sector properties"); + foreach (Sector s in sel) + { + BuilderPlug.Me.CopiedSectorProps.Apply(s); + s.UpdateCeilingSurface(); + s.UpdateFloorSurface(); + } + General.Interface.DisplayStatus(StatusType.Action, "Pasted sector properties."); + + // Update and redraw + General.Map.IsChanged = true; + General.Interface.RefreshInfo(); + General.Interface.RedrawDisplay(); + } + } + } + + // This creates a new vertex at the mouse position + [BeginAction("insertitem", BaseAction = true)] + public virtual void InsertVertexAction() + { + // Start drawing mode + DrawGeometryMode drawmode = new DrawGeometryMode(); + if (mouseinside) + { + bool snaptogrid = General.Interface.ShiftState ^ General.Interface.SnapToGrid; + bool snaptonearest = General.Interface.CtrlState ^ General.Interface.AutoMerge; + DrawnVertex v = DrawGeometryMode.GetCurrentPosition(mousemappos, snaptonearest, snaptogrid, renderer, new List()); + drawmode.DrawPointAt(v); + } + General.Editing.ChangeMode(drawmode); + } + + [BeginAction("makedoor")] + public void MakeDoor() + { + // Highlighted item not selected? + if ((highlighted != null) && !highlighted.Selected) + { + // Make this the only selection + General.Map.Map.ClearSelectedSectors(); + General.Map.Map.ClearSelectedLinedefs(); + SelectSector(highlighted, true, false); + General.Interface.RedrawDisplay(); + } + + // Anything selected? + ICollection orderedselection = General.Map.Map.GetSelectedSectors(true); + if (orderedselection.Count > 0) + { + string doortex = ""; + string floortex = null; + string ceiltex = null; + + // Find ceiling and floor textures + foreach (Sector s in orderedselection) + { + if (floortex == null) floortex = s.FloorTexture; else if (floortex != s.FloorTexture) floortex = ""; + if (ceiltex == null) ceiltex = s.CeilTexture; else if (ceiltex != s.CeilTexture) ceiltex = ""; + } + + // Show the dialog + MakeDoorForm form = new MakeDoorForm(); + if (form.Show(General.Interface, doortex, ceiltex, floortex) == DialogResult.OK) + { + doortex = form.DoorTexture; + ceiltex = form.CeilingTexture; + floortex = form.FloorTexture; + + // Create undo + General.Map.UndoRedo.CreateUndo("Make door (" + doortex + ")"); + General.Interface.DisplayStatus(StatusType.Action, "Created a " + doortex + " door."); + + // Go for all selected sectors + foreach (Sector s in orderedselection) + { + // Lower the ceiling down to the floor + s.CeilHeight = s.FloorHeight; + + // Make a unique tag (not sure if we need it yet, depends on the args) + int tag = General.Map.Map.GetNewTag(); + + // Go for all it's sidedefs + foreach (Sidedef sd in s.Sidedefs) + { + // Singlesided? + if (sd.Other == null) + { + // Make this a doortrak + sd.SetTextureHigh("-"); + sd.SetTextureMid(General.Map.Config.MakeDoorTrack); + sd.SetTextureLow("-"); + + // Set upper/lower unpegged flags + sd.Line.SetFlag(General.Map.Config.UpperUnpeggedFlag, false); + sd.Line.SetFlag(General.Map.Config.LowerUnpeggedFlag, true); + } + else + { + // Set textures + if (floortex.Length > 0) s.SetFloorTexture(floortex); + if (ceiltex.Length > 0) s.SetCeilTexture(ceiltex); + if (doortex.Length > 0) sd.Other.SetTextureHigh(doortex); + + // Set upper/lower unpegged flags + sd.Line.SetFlag(General.Map.Config.UpperUnpeggedFlag, false); + sd.Line.SetFlag(General.Map.Config.LowerUnpeggedFlag, false); + + // Get door linedef type from config + sd.Line.Action = General.Map.Config.MakeDoorAction; + + // Set the linedef args + for (int i = 0; i < Linedef.NUM_ARGS; i++) + { + // A -1 arg indicates that the arg must be set to the new sector tag + // and only in this case we set the tag on the sector, because only + // then we know for sure that we need a tag. + if (General.Map.Config.MakeDoorArgs[i] == -1) + { + sd.Line.Args[i] = tag; + s.Tag = tag; + } + else + { + sd.Line.Args[i] = General.Map.Config.MakeDoorArgs[i]; + } + } + + // Make sure the line is facing outwards + if (sd.IsFront) + { + sd.Line.FlipVertices(); + sd.Line.FlipSidedefs(); + } + } + } + } + + // When a single sector was selected, deselect it now + if (orderedselection.Count == 1) + { + orderedselection.Clear(); + General.Map.Map.ClearSelectedSectors(); + General.Map.Map.ClearSelectedLinedefs(); + } + } + + // Done + form.Dispose(); + General.Interface.RedrawDisplay(); + } + } + + [BeginAction("deleteitem", BaseAction = true)] + public void DeleteItem() + { + // Make list of selected sectors + List selected = new List(General.Map.Map.GetSelectedSectors(true)); + if ((selected.Count == 0) && (highlighted != null) && !highlighted.IsDisposed) selected.Add(highlighted); + + // Anything to do? + if (selected.Count > 0) + { + // Make undo + if (selected.Count > 1) + { + General.Map.UndoRedo.CreateUndo("Delete " + selected.Count + " sectors"); + General.Interface.DisplayStatus(StatusType.Action, "Deleted " + selected.Count + " sectors."); + } + else + { + General.Map.UndoRedo.CreateUndo("Delete sector"); + General.Interface.DisplayStatus(StatusType.Action, "Deleted sector."); + } + + // Dispose selected sectors + foreach (Sector s in selected) + { + // Get all the linedefs + General.Map.Map.ClearMarkedLinedefs(false); + foreach (Sidedef sd in s.Sidedefs) sd.Line.Marked = true; + List lines = General.Map.Map.GetMarkedLinedefs(true); + + // Dispose the sector + s.Dispose(); + + // Check all the lines + for (int i = lines.Count - 1; i >= 0; i--) + { + // If the line has become orphaned, remove it + if ((lines[i].Front == null) && (lines[i].Back == null)) + { + // Remove line + lines[i].Dispose(); + } + else + { + // If the line only has a back side left, flip the line and sides + if ((lines[i].Front == null) && (lines[i].Back != null)) + { + lines[i].FlipVertices(); + lines[i].FlipSidedefs(); + } + + // Update sided flags + lines[i].ApplySidedFlags(); + } + } + } + + // Update cache values + General.Map.IsChanged = true; + General.Map.Map.Update(); + + // Make text labels for sectors + SetupLabels(); + UpdateSelectedLabels(); + + // Redraw screen + General.Interface.RedrawDisplay(); + } + } + + // This joins sectors together and keeps all lines + [BeginAction("joinsectors")] + public void JoinSectors() + { + // Worth our money? + int count = General.Map.Map.GetSelectedSectors(true).Count; + if (count > 1) + { + // Make undo + General.Map.UndoRedo.CreateUndo("Join " + count + " sectors"); + General.Interface.DisplayStatus(StatusType.Action, "Joined " + count + " sectors."); + + // Merge + JoinMergeSectors(false); + + // Deselect + General.Map.Map.ClearSelectedSectors(); + General.Map.Map.ClearSelectedLinedefs(); + General.Map.IsChanged = true; + + // Redraw display + General.Interface.RedrawDisplay(); + } + } + + // This joins sectors together and removes the lines in between + [BeginAction("mergesectors")] + public void MergeSectors() + { + // Worth our money? + int count = General.Map.Map.GetSelectedSectors(true).Count; + if (count > 1) + { + // Make undo + General.Map.UndoRedo.CreateUndo("Merge " + count + " sectors"); + General.Interface.DisplayStatus(StatusType.Action, "Merged " + count + " sectors."); + + // Merge + JoinMergeSectors(true); + + // Deselect + General.Map.Map.ClearSelectedSectors(); + General.Map.Map.ClearSelectedLinedefs(); + General.Map.IsChanged = true; + + // Redraw display + General.Interface.RedrawDisplay(); + } + } + + private Lights InterpolateLight(Lights start, Lights end, int delta) + { + int r, g, b; + + r = start.color.r + (delta * (end.color.r - start.color.r)) / 256; + g = start.color.g + (delta * (end.color.g - start.color.g)) / 256; + b = start.color.b + (delta * (end.color.b - start.color.b)) / 256; + + return new Lights((byte)r, (byte)g, (byte)b, 0); + } + + // Make gradient brightness + // villsa 9/14/11 (builder64) - new sector color gradients + [BeginAction("gradientbrightness")] + public void MakeGradientBrightness() + { + General.Interface.DisplayStatus(StatusType.Action, "Created gradient colors over selected sectors."); + General.Map.UndoRedo.CreateUndo("Gradient brightness"); + + // Need at least 3 selected sectors + // The first and last are not modified + ICollection orderedselection = General.Map.Map.GetSelectedSectors(true); + if (orderedselection.Count > 2) + { + Lights startlight; + Lights endlight; + int delta = (256 / (orderedselection.Count - 1)); + int index; + + // FLOOR + startlight = (Lights)General.GetByIndex(orderedselection, 0).FloorColor; + endlight = (Lights)General.GetByIndex(orderedselection, orderedselection.Count - 1).FloorColor; + index = 0; + + // Go for all sectors in between first and last + foreach (Sector s in orderedselection) + { + s.FloorColor = InterpolateLight(startlight, endlight, index); + index += delta; + } + + // CEILING + startlight = (Lights)General.GetByIndex(orderedselection, 0).CeilColor; + endlight = (Lights)General.GetByIndex(orderedselection, orderedselection.Count - 1).CeilColor; + index = 0; + + // Go for all sectors in between first and last + foreach (Sector s in orderedselection) + { + s.CeilColor = InterpolateLight(startlight, endlight, index); + index += delta; + } + + // THING + startlight = (Lights)General.GetByIndex(orderedselection, 0).ThingColor; + endlight = (Lights)General.GetByIndex(orderedselection, orderedselection.Count - 1).ThingColor; + index = 0; + + // Go for all sectors in between first and last + foreach (Sector s in orderedselection) + { + s.ThingColor = InterpolateLight(startlight, endlight, index); + index += delta; + } + + // TOP + startlight = (Lights)General.GetByIndex(orderedselection, 0).TopColor; + endlight = (Lights)General.GetByIndex(orderedselection, orderedselection.Count - 1).TopColor; + index = 0; + + // Go for all sectors in between first and last + foreach (Sector s in orderedselection) + { + s.TopColor = InterpolateLight(startlight, endlight, index); + index += delta; + } + + // BOTTOM + startlight = (Lights)General.GetByIndex(orderedselection, 0).LowerColor; + endlight = (Lights)General.GetByIndex(orderedselection, orderedselection.Count - 1).LowerColor; + index = 0; + + // Go for all sectors in between first and last + foreach (Sector s in orderedselection) + { + s.LowerColor = InterpolateLight(startlight, endlight, index); + index += delta; + } + } + + // Update + General.Map.Map.Update(); + UpdateOverlay(); + renderer.Present(); + General.Interface.RedrawDisplay(); + General.Interface.RefreshInfo(); + General.Map.IsChanged = true; + } + + // Make gradient floors + [BeginAction("gradientfloors")] + public void MakeGradientFloors() + { + General.Interface.DisplayStatus(StatusType.Action, "Created gradient floor heights over selected sectors."); + General.Map.UndoRedo.CreateUndo("Gradient floor heights"); + + // Need at least 3 selected sectors + // The first and last are not modified + ICollection orderedselection = General.Map.Map.GetSelectedSectors(true); + if (orderedselection.Count > 2) + { + float startlevel = (float)General.GetByIndex(orderedselection, 0).FloorHeight; + float endlevel = (float)General.GetByIndex(orderedselection, orderedselection.Count - 1).FloorHeight; + float delta = endlevel - startlevel; + + // Go for all sectors in between first and last + int index = 0; + foreach (Sector s in orderedselection) + { + float u = (float)index / (float)(orderedselection.Count - 1); + float b = startlevel + delta * u; + s.FloorHeight = (int)b; + index++; + } + } + + // Update + General.Interface.RefreshInfo(); + General.Map.IsChanged = true; + } + + // Make gradient ceilings + [BeginAction("gradientceilings")] + public void MakeGradientCeilings() + { + General.Interface.DisplayStatus(StatusType.Action, "Created gradient ceiling heights over selected sectors."); + General.Map.UndoRedo.CreateUndo("Gradient ceiling heights"); + + // Need at least 3 selected sectors + // The first and last are not modified + ICollection orderedselection = General.Map.Map.GetSelectedSectors(true); + if (orderedselection.Count > 2) + { + float startlevel = (float)General.GetByIndex(orderedselection, 0).CeilHeight; + float endlevel = (float)General.GetByIndex(orderedselection, orderedselection.Count - 1).CeilHeight; + float delta = endlevel - startlevel; + + // Go for all sectors in between first and last + int index = 0; + foreach (Sector s in orderedselection) + { + float u = (float)index / (float)(orderedselection.Count - 1); + float b = startlevel + delta * u; + s.CeilHeight = (int)b; + index++; + } + } + + // Update + General.Interface.RefreshInfo(); + General.Map.IsChanged = true; + } + + // Change heights + [BeginAction("lowerfloor8")] + public void LowerFloors8() + { + General.Interface.DisplayStatus(StatusType.Action, "Lowered floor heights by 8mp."); + General.Map.UndoRedo.CreateUndo("Floor heights change", this, UndoGroup.FloorHeightChange, CreateSelectionCRC()); + + // Change heights + ICollection selected = General.Map.Map.GetSelectedSectors(true); + if ((selected.Count == 0) && (highlighted != null) && !highlighted.IsDisposed) selected.Add(highlighted); + foreach (Sector s in selected) + { + s.FloorHeight -= 8; + } + + // Update + General.Interface.RefreshInfo(); + General.Map.IsChanged = true; + } + + // Change heights + [BeginAction("raisefloor8")] + public void RaiseFloors8() + { + General.Interface.DisplayStatus(StatusType.Action, "Raised floor heights by 8mp."); + General.Map.UndoRedo.CreateUndo("Floor heights change", this, UndoGroup.FloorHeightChange, CreateSelectionCRC()); + + // Change heights + ICollection selected = General.Map.Map.GetSelectedSectors(true); + if ((selected.Count == 0) && (highlighted != null) && !highlighted.IsDisposed) selected.Add(highlighted); + foreach (Sector s in selected) + { + s.FloorHeight += 8; + } + + // Update + General.Interface.RefreshInfo(); + General.Map.IsChanged = true; + } + + // Change heights + [BeginAction("lowerceiling8")] + public void LowerCeilings8() + { + General.Interface.DisplayStatus(StatusType.Action, "Lowered ceiling heights by 8mp."); + General.Map.UndoRedo.CreateUndo("Ceiling heights change", this, UndoGroup.CeilingHeightChange, CreateSelectionCRC()); + + // Change heights + ICollection selected = General.Map.Map.GetSelectedSectors(true); + if ((selected.Count == 0) && (highlighted != null) && !highlighted.IsDisposed) selected.Add(highlighted); + foreach (Sector s in selected) + { + s.CeilHeight -= 8; + } + + // Update + General.Interface.RefreshInfo(); + General.Map.IsChanged = true; + } + + // Change heights + [BeginAction("raiseceiling8")] + public void RaiseCeilings8() + { + General.Interface.DisplayStatus(StatusType.Action, "Raised ceiling heights by 8mp."); + General.Map.UndoRedo.CreateUndo("Ceiling heights change", this, UndoGroup.CeilingHeightChange, CreateSelectionCRC()); + + // Change heights + ICollection selected = General.Map.Map.GetSelectedSectors(true); + if ((selected.Count == 0) && (highlighted != null) && !highlighted.IsDisposed) selected.Add(highlighted); + foreach (Sector s in selected) + { + s.CeilHeight += 8; + } + + // Update + General.Interface.RefreshInfo(); + General.Map.IsChanged = true; + } + + // This clears the selection + [BeginAction("clearselection", BaseAction = true)] + public void ClearSelection() + { + // Clear selection + General.Map.Map.ClearAllSelected(); + + // Clear labels + foreach (TextLabel[] labelarray in labels.Values) + foreach (TextLabel l in labelarray) l.Text = ""; + + // Redraw + General.Interface.RedrawDisplay(); + } + + #endregion + } +} diff --git a/Source/Plugins/BuilderModes/ClassicModes/ThingsMode.cs b/Source/Plugins/BuilderModes/ClassicModes/ThingsMode.cs new file mode 100644 index 0000000..23dcb30 --- /dev/null +++ b/Source/Plugins/BuilderModes/ClassicModes/ThingsMode.cs @@ -0,0 +1,803 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using CodeImp.DoomBuilder.Windows; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Geometry; +using CodeImp.DoomBuilder.Editing; +using CodeImp.DoomBuilder.Actions; +using CodeImp.DoomBuilder.Config; +using CodeImp.DoomBuilder.Types; + +#endregion + +namespace CodeImp.DoomBuilder.BuilderModes +{ + [EditMode(DisplayName = "Things Mode", + SwitchAction = "thingsmode", // Action name used to switch to this mode + ButtonImage = "ThingsMode.png", // Image resource name for the button + ButtonOrder = int.MinValue + 300, // Position of the button (lower is more to the left) + ButtonGroup = "000_editing", + UseByDefault = true, + SafeStartMode = true)] + + public class ThingsMode : BaseClassicMode + { + #region ================== Constants + + #endregion + + #region ================== Variables + + // Highlighted item + private Thing highlighted; + private Association[] association = new Association[Thing.NUM_ARGS]; + private Association highlightasso = new Association(); + + // Interface + private bool editpressed; + private bool thinginserted; + + #endregion + + #region ================== Properties + + public override object HighlightedObject { get { return highlighted; } } + + #endregion + + #region ================== Constructor / Disposer + + #endregion + + #region ================== Methods + + public override void OnHelp() + { + General.ShowHelp("e_things.html"); + } + + // Cancel mode + public override void OnCancel() + { + base.OnCancel(); + + // Return to this mode + General.Editing.ChangeMode(new ThingsMode()); + } + + // Mode engages + public override void OnEngage() + { + base.OnEngage(); + renderer.SetPresentation(Presentation.Things); + + // Add toolbar buttons + General.Interface.AddButton(BuilderPlug.Me.MenusForm.CopyProperties); + General.Interface.AddButton(BuilderPlug.Me.MenusForm.PasteProperties); + + // Convert geometry selection to linedefs selection + General.Map.Map.ConvertSelection(SelectionType.Linedefs); + General.Map.Map.SelectionType = SelectionType.Things; + } + + // Mode disengages + public override void OnDisengage() + { + base.OnDisengage(); + + // Remove toolbar buttons + General.Interface.RemoveButton(BuilderPlug.Me.MenusForm.CopyProperties); + General.Interface.RemoveButton(BuilderPlug.Me.MenusForm.PasteProperties); + + // Going to EditSelectionMode? + if (General.Editing.NewMode is EditSelectionMode) + { + // Not pasting anything? + EditSelectionMode editmode = (General.Editing.NewMode as EditSelectionMode); + if (!editmode.Pasting) + { + // No selection made? But we have a highlight! + if ((General.Map.Map.GetSelectedThings(true).Count == 0) && (highlighted != null)) + { + // Make the highlight the selection + highlighted.Selected = true; + } + } + } + + // Hide highlight info + General.Interface.HideInfo(); + } + + // This redraws the display + public override void OnRedrawDisplay() + { + renderer.RedrawSurface(); + + // Render lines and vertices + if (renderer.StartPlotter(true)) + { + renderer.PlotLinedefSet(General.Map.Map.Linedefs); + renderer.PlotVerticesSet(General.Map.Map.Vertices); + for (int i = 0; i < Thing.NUM_ARGS; i++) BuilderPlug.Me.PlotAssociations(renderer, association[i]); + if ((highlighted != null) && !highlighted.IsDisposed) BuilderPlug.Me.PlotReverseAssociations(renderer, highlightasso); + renderer.Finish(); + } + + // Render things + if (renderer.StartThings(true)) + { + renderer.RenderThingSet(General.Map.ThingsFilter.HiddenThings, Presentation.THINGS_HIDDEN_ALPHA); + renderer.RenderThingSet(General.Map.ThingsFilter.VisibleThings, 1.0f); + for (int i = 0; i < Thing.NUM_ARGS; i++) BuilderPlug.Me.RenderAssociations(renderer, association[i]); + if ((highlighted != null) && !highlighted.IsDisposed) + { + BuilderPlug.Me.RenderReverseAssociations(renderer, highlightasso); + renderer.RenderThing(highlighted, General.Colors.Highlight, 1.0f); + } + renderer.Finish(); + } + + // Selecting? + if (selecting) + { + // Render selection + if (renderer.StartOverlay(true)) + { + RenderMultiSelection(); + renderer.Finish(); + } + } + + renderer.Present(); + } + + // This highlights a new item + protected void Highlight(Thing t) + { + bool completeredraw = false; + LinedefActionInfo action = null; + + // Often we can get away by simply undrawing the previous + // highlight and drawing the new highlight. But if associations + // are or were drawn we need to redraw the entire display. + + // Previous association highlights something? + if ((highlighted != null) && (highlighted.Tag > 0)) completeredraw = true; + + // Set highlight association + if (t != null) + highlightasso.Set(t.Tag, UniversalType.ThingTag); + else + highlightasso.Set(0, 0); + + // New association highlights something? + if ((t != null) && (t.Tag > 0)) completeredraw = true; + + if (t != null) + { + // Check if we can find the linedefs action + if ((t.Action > 0) && General.Map.Config.LinedefActions.ContainsKey(t.Action)) + action = General.Map.Config.LinedefActions[t.Action]; + } + + // Determine linedef associations + for (int i = 0; i < Thing.NUM_ARGS; i++) + { + // Previous association highlights something? + if ((association[i].type == UniversalType.SectorTag) || + (association[i].type == UniversalType.LinedefTag) || + (association[i].type == UniversalType.ThingTag)) completeredraw = true; + + // Make new association + if (action != null) + association[i].Set(t.Args[i], action.Args[i].Type); + else + association[i].Set(0, 0); + + // New association highlights something? + if ((association[i].type == UniversalType.SectorTag) || + (association[i].type == UniversalType.LinedefTag) || + (association[i].type == UniversalType.ThingTag)) completeredraw = true; + } + + // If we're changing associations, then we + // need to redraw the entire display + if (completeredraw) + { + // Set new highlight and redraw completely + highlighted = t; + General.Interface.RedrawDisplay(); + } + else + { + // Update display + if (renderer.StartThings(false)) + { + // Undraw previous highlight + if ((highlighted != null) && !highlighted.IsDisposed) + renderer.RenderThing(highlighted, renderer.DetermineThingColor(highlighted), 1.0f); + + // Set new highlight + highlighted = t; + + // Render highlighted item + if ((highlighted != null) && !highlighted.IsDisposed) + renderer.RenderThing(highlighted, General.Colors.Highlight, 1.0f); + + // Done + renderer.Finish(); + renderer.Present(); + } + } + + // Show highlight info + if ((highlighted != null) && !highlighted.IsDisposed) + General.Interface.ShowThingInfo(highlighted); + else + General.Interface.HideInfo(); + } + + // Selection + protected override void OnSelectBegin() + { + // Item highlighted? + if ((highlighted != null) && !highlighted.IsDisposed) + { + // Flip selection + highlighted.Selected = !highlighted.Selected; + + // Update display + if (renderer.StartThings(false)) + { + // Redraw highlight to show selection + renderer.RenderThing(highlighted, renderer.DetermineThingColor(highlighted), 1.0f); + renderer.Finish(); + renderer.Present(); + } + } + else + { + // Start making a selection + StartMultiSelection(); + } + + base.OnSelectBegin(); + } + + // End selection + protected override void OnSelectEnd() + { + // Not ending from a multi-selection? + if (!selecting) + { + // Item highlighted? + if ((highlighted != null) && !highlighted.IsDisposed) + { + // Update display + if (renderer.StartThings(false)) + { + // Render highlighted item + renderer.RenderThing(highlighted, General.Colors.Highlight, 1.0f); + renderer.Finish(); + renderer.Present(); + } + } + } + + base.OnSelectEnd(); + } + + // Start editing + protected override void OnEditBegin() + { + thinginserted = false; + + // Item highlighted? + if ((highlighted != null) && !highlighted.IsDisposed) + { + // Edit pressed in this mode + editpressed = true; + + // Highlighted item not selected? + if (!highlighted.Selected && (BuilderPlug.Me.AutoClearSelection || (General.Map.Map.SelectedThingsCount == 0))) + { + // Make this the only selection + General.Map.Map.ClearSelectedThings(); + highlighted.Selected = true; + General.Interface.RedrawDisplay(); + } + + // Update display + if (renderer.StartThings(false)) + { + // Redraw highlight to show selection + renderer.RenderThing(highlighted, renderer.DetermineThingColor(highlighted), 1.0f); + renderer.Finish(); + renderer.Present(); + } + } + else + { + // Mouse in window? + if (mouseinside) + { + // Edit pressed in this mode + editpressed = true; + thinginserted = true; + + // Insert a new item and select it for dragging + General.Map.UndoRedo.CreateUndo("Insert thing"); + Thing t = InsertThing(mousemappos); + + if (t == null) + { + General.Map.UndoRedo.WithdrawUndo(); + } + else + { + General.Map.Map.ClearSelectedThings(); + t.Selected = true; + Highlight(t); + General.Interface.RedrawDisplay(); + } + } + } + + base.OnEditBegin(); + } + + // Done editing + protected override void OnEditEnd() + { + // Edit pressed in this mode? + if (editpressed) + { + // Anything selected? + ICollection selected = General.Map.Map.GetSelectedThings(true); + if (selected.Count > 0) + { + if (General.Interface.IsActiveWindow) + { + // Edit only when preferred + if (!thinginserted || BuilderPlug.Me.EditNewThing) + { + // Show thing edit dialog + General.Interface.ShowEditThings(selected); + + // When a single thing was selected, deselect it now + if (selected.Count == 1) General.Map.Map.ClearSelectedThings(); + + // Update things filter + General.Map.ThingsFilter.Update(); + + // Update entire display + General.Interface.RedrawDisplay(); + } + } + } + } + + editpressed = false; + base.OnEditEnd(); + } + + // Mouse moves + public override void OnMouseMove(MouseEventArgs e) + { + base.OnMouseMove(e); + + // Not holding any buttons? + if (e.Button == MouseButtons.None) + { + // Find the nearest thing within highlight range + Thing t = MapSet.NearestThingSquareRange(General.Map.ThingsFilter.VisibleThings, mousemappos, BuilderPlug.Me.HighlightThingsRange / renderer.Scale); + + // Highlight if not the same + if (t != highlighted) Highlight(t); + } + } + + // Mouse leaves + public override void OnMouseLeave(EventArgs e) + { + base.OnMouseLeave(e); + + // Highlight nothing + Highlight(null); + } + + // Mouse wants to drag + protected override void OnDragStart(MouseEventArgs e) + { + base.OnDragStart(e); + + // Edit button used? + if (General.Actions.CheckActionActive(null, "classicedit")) + { + // Anything highlighted? + if ((highlighted != null) && !highlighted.IsDisposed) + { + // Highlighted item not selected? + if (!highlighted.Selected) + { + // Select only this sector for dragging + General.Map.Map.ClearSelectedThings(); + highlighted.Selected = true; + } + + // Start dragging the selection + General.Editing.ChangeMode(new DragThingsMode(new ThingsMode(), mousedownmappos)); + } + } + } + + // This is called wheh selection ends + protected override void OnEndMultiSelection() + { + bool selectionvolume = ((Math.Abs(base.selectionrect.Width) > 0.1f) && (Math.Abs(base.selectionrect.Height) > 0.1f)); + + if (BuilderPlug.Me.AutoClearSelection && !selectionvolume) + General.Map.Map.ClearSelectedThings(); + + if (selectionvolume) + { + if (General.Interface.ShiftState ^ BuilderPlug.Me.AdditiveSelect) + { + // Go for all things + foreach (Thing t in General.Map.ThingsFilter.VisibleThings) + { + t.Selected |= ((t.Position.x >= selectionrect.Left) && + (t.Position.y >= selectionrect.Top) && + (t.Position.x <= selectionrect.Right) && + (t.Position.y <= selectionrect.Bottom)); + } + } + else + { + // Go for all things + foreach (Thing t in General.Map.ThingsFilter.VisibleThings) + { + t.Selected = ((t.Position.x >= selectionrect.Left) && + (t.Position.y >= selectionrect.Top) && + (t.Position.x <= selectionrect.Right) && + (t.Position.y <= selectionrect.Bottom)); + } + } + } + + base.OnEndMultiSelection(); + + // Clear overlay + if (renderer.StartOverlay(true)) renderer.Finish(); + + // Redraw + General.Interface.RedrawDisplay(); + } + + // This is called when the selection is updated + protected override void OnUpdateMultiSelection() + { + base.OnUpdateMultiSelection(); + + // Render selection + if (renderer.StartOverlay(true)) + { + RenderMultiSelection(); + renderer.Finish(); + renderer.Present(); + } + } + + // When copying + public override bool OnCopyBegin() + { + // No selection made? But we have a highlight! + if ((General.Map.Map.GetSelectedThings(true).Count == 0) && (highlighted != null)) + { + // Make the highlight the selection + highlighted.Selected = true; + } + + return base.OnCopyBegin(); + } + + #endregion + + #region ================== Actions + + // This copies the properties + [BeginAction("classiccopyproperties")] + public void CopyProperties() + { + // Determine source things + ICollection sel = null; + if (General.Map.Map.SelectedThingsCount > 0) + sel = General.Map.Map.GetSelectedThings(true); + else if (highlighted != null) + { + sel = new List(); + sel.Add(highlighted); + } + + if (sel != null) + { + // Copy properties from first source thing + BuilderPlug.Me.CopiedThingProps = new ThingProperties(General.GetByIndex(sel, 0)); + General.Interface.DisplayStatus(StatusType.Action, "Copied thing properties."); + } + } + + // This pastes the properties + [BeginAction("classicpasteproperties")] + public void PasteProperties() + { + if (BuilderPlug.Me.CopiedThingProps != null) + { + // Determine target things + ICollection sel = null; + if (General.Map.Map.SelectedThingsCount > 0) + sel = General.Map.Map.GetSelectedThings(true); + else if (highlighted != null) + { + sel = new List(); + sel.Add(highlighted); + } + + if (sel != null) + { + // Apply properties to selection + General.Map.UndoRedo.CreateUndo("Paste thing properties"); + foreach (Thing t in sel) + { + BuilderPlug.Me.CopiedThingProps.Apply(t); + t.UpdateConfiguration(); + } + General.Interface.DisplayStatus(StatusType.Action, "Pasted thing properties."); + + // Update and redraw + General.Map.IsChanged = true; + General.Map.ThingsFilter.Update(); + General.Interface.RefreshInfo(); + General.Interface.RedrawDisplay(); + } + } + } + + // This clears the selection + [BeginAction("clearselection", BaseAction = true)] + public void ClearSelection() + { + // Clear selection + General.Map.Map.ClearAllSelected(); + + // Redraw + General.Interface.RedrawDisplay(); + } + + // This creates a new thing at the mouse position + [BeginAction("insertitem", BaseAction = true)] + public virtual void InsertThing() + { + // Mouse in window? + if (mouseinside) + { + // Insert new thing + General.Map.UndoRedo.CreateUndo("Insert thing"); + Thing t = InsertThing(mousemappos); + + if (t == null) + { + General.Map.UndoRedo.WithdrawUndo(); + return; + } + + // Edit the thing? + if (BuilderPlug.Me.EditNewThing) + { + // Redraw screen + General.Interface.RedrawDisplay(); + + List things = new List(1); + things.Add(t); + + // villsa (builder64) - don't bring up edit menu. Useful for quickly inserting things + //General.Interface.ShowEditThings(things); + } + + General.Interface.DisplayStatus(StatusType.Action, "Inserted a new thing."); + + // Update things filter + General.Map.ThingsFilter.Update(); + + // Redraw screen + General.Interface.RedrawDisplay(); + } + } + + // This creates a new thing + private Thing InsertThing(Vector2D pos) + { + if (pos.x < General.Map.Config.LeftBoundary || pos.x > General.Map.Config.RightBoundary || + pos.y > General.Map.Config.TopBoundary || pos.y < General.Map.Config.BottomBoundary) + { + General.Interface.DisplayStatus(StatusType.Warning, "Failed to insert thing: outside of map boundaries."); + return null; + } + + // Create thing + Thing t = General.Map.Map.CreateThing(); + if (t != null) + { + General.Settings.ApplyDefaultThingSettings(t); + + t.Move(pos); + + t.UpdateConfiguration(); + + // Update things filter so that it includes this thing + General.Map.ThingsFilter.Update(); + + // Snap to grid enabled? + if (General.Interface.SnapToGrid) + { + // Snap to grid + t.SnapToGrid(); + } + else + { + // Snap to map format accuracy + t.SnapToAccuracy(); + } + } + + return t; + } + + // villsa 9/13/11 (builder64) + [BeginAction("thingrotateleft", BaseAction = true)] + public void ThingRotateLeft() + { + // Make list of selected things + List selected = new List(General.Map.Map.GetSelectedThings(true)); + if ((selected.Count == 0) && (highlighted != null) && !highlighted.IsDisposed) selected.Add(highlighted); + + // Anything to do? + if (selected.Count > 0) + { + // Make undo + if (selected.Count > 1) + { + General.Map.UndoRedo.CreateUndo("Rotate " + selected.Count + " things"); + General.Interface.DisplayStatus(StatusType.Action, "Rotated " + selected.Count + " things."); + } + else + { + General.Map.UndoRedo.CreateUndo("Rotate thing"); + General.Interface.DisplayStatus(StatusType.Action, "Rotated a thing."); + } + + foreach (Thing t in selected) + { + int angle; + + angle = Angle2D.RealToDoom(t.Angle); + angle += 45; + + t.Rotate(Angle2D.DoomToReal(angle)); + } + + General.Map.IsChanged = true; + General.Map.ThingsFilter.Update(); + General.Interface.RefreshInfo(); + General.Interface.RedrawDisplay(); + } + } + + // villsa 9/13/11 (builder64) + [BeginAction("thingrotateright", BaseAction = true)] + public void ThingRotateRight() + { + // Make list of selected things + List selected = new List(General.Map.Map.GetSelectedThings(true)); + if ((selected.Count == 0) && (highlighted != null) && !highlighted.IsDisposed) selected.Add(highlighted); + + // Anything to do? + if (selected.Count > 0) + { + // Make undo + if (selected.Count > 1) + { + General.Map.UndoRedo.CreateUndo("Rotate " + selected.Count + " things"); + General.Interface.DisplayStatus(StatusType.Action, "Rotated " + selected.Count + " things."); + } + else + { + General.Map.UndoRedo.CreateUndo("Rotate thing"); + General.Interface.DisplayStatus(StatusType.Action, "Rotated a thing."); + } + + foreach (Thing t in selected) + { + int angle; + + angle = Angle2D.RealToDoom(t.Angle); + angle -= 45; + + t.Rotate(Angle2D.DoomToReal(angle)); + } + + General.Map.IsChanged = true; + General.Map.ThingsFilter.Update(); + General.Interface.RefreshInfo(); + General.Interface.RedrawDisplay(); + } + } + + [BeginAction("deleteitem", BaseAction = true)] + public void DeleteItem() + { + // Make list of selected things + List selected = new List(General.Map.Map.GetSelectedThings(true)); + if ((selected.Count == 0) && (highlighted != null) && !highlighted.IsDisposed) selected.Add(highlighted); + + // Anything to do? + if (selected.Count > 0) + { + // Make undo + if (selected.Count > 1) + { + General.Map.UndoRedo.CreateUndo("Delete " + selected.Count + " things"); + General.Interface.DisplayStatus(StatusType.Action, "Deleted " + selected.Count + " things."); + } + else + { + General.Map.UndoRedo.CreateUndo("Delete thing"); + General.Interface.DisplayStatus(StatusType.Action, "Deleted a thing."); + } + + // Dispose selected things + foreach (Thing t in selected) t.Dispose(); + + // Update cache values + General.Map.IsChanged = true; + General.Map.ThingsFilter.Update(); + + // Invoke a new mousemove so that the highlighted item updates + MouseEventArgs e = new MouseEventArgs(MouseButtons.None, 0, (int)mousepos.x, (int)mousepos.y, 0); + OnMouseMove(e); + + // Redraw screen + General.Interface.RedrawDisplay(); + } + } + + #endregion + } +} diff --git a/Source/Plugins/BuilderModes/ClassicModes/TriangulatorMode.cs b/Source/Plugins/BuilderModes/ClassicModes/TriangulatorMode.cs new file mode 100644 index 0000000..12f4cde --- /dev/null +++ b/Source/Plugins/BuilderModes/ClassicModes/TriangulatorMode.cs @@ -0,0 +1,584 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using CodeImp.DoomBuilder.Windows; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Geometry; +using CodeImp.DoomBuilder.Editing; +using System.Threading; +using System.Drawing; + +#endregion + +namespace CodeImp.DoomBuilder.BuilderModes.Editing +{ + #if DEBUG + + [EditMode(DisplayName = "Triangulator Mode", + SwitchAction = "triangulatormode", // Action name used to switch to this mode + ButtonImage = "TriangulatorMode.png", // Image resource name for the button + ButtonOrder = int.MaxValue)] // Position of the button (lower is more to the left) + + public class TriangulatorMode : ClassicMode + { + #region ================== Constants + + #endregion + + #region ================== Variables + + // Highlighted item + private Sector highlighted; + + // Labels + private ICollection labelpos; + + #endregion + + #region ================== Properties + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + public TriangulatorMode() + { + } + + // Disposer + public override void Dispose() + { + // Not already disposed? + if(!isdisposed) + { + // Clean up + + // Dispose base + base.Dispose(); + } + } + + #endregion + + #region ================== Methods + + // Cancel mode + public override void OnCancel() + { + base.OnCancel(); + + // Return to this mode + General.Editing.ChangeMode(new SectorsMode()); + } + + // Mode engages + public override void OnEngage() + { + base.OnEngage(); + } + + // Mode disengages + public override void OnDisengage() + { + base.OnDisengage(); + + // Check which mode we are switching to + if(General.Editing.NewMode is VerticesMode) + { + // Convert selection to vertices + + // Clear selected sectors + General.Map.Map.ClearSelectedSectors(); + } + else if(General.Editing.NewMode is LinedefsMode) + { + // Convert selection to linedefs + + // Clear selected sectors + General.Map.Map.ClearSelectedSectors(); + } + + // Hide highlight info + General.Interface.HideInfo(); + } + + // This redraws the display + public unsafe override void OnRedrawDisplay() + { + // Render lines and vertices + if(renderer.StartPlotter(true)) + { + renderer.PlotLinedefSet(General.Map.Map.Linedefs); + renderer.PlotVerticesSet(General.Map.Map.Vertices); + if((highlighted != null) && !highlighted.IsDisposed) + renderer.PlotSector(highlighted, General.Colors.Highlight); + renderer.Finish(); + } + + // Render labels + /* + if(renderer.StartOverlay(true)) + { + if(labelpos != null) + { + foreach(LabelPositionInfo lb in labelpos) + renderer.RenderRectangleFilled(new RectangleF(lb.position.x - lb.radius / 2, lb.position.y - lb.radius / 2, lb.radius, lb.radius), General.Colors.Indication, true); + } + + renderer.Finish(); + } + */ + + // Do not show things + if(renderer.StartThings(true)) + { + renderer.Finish(); + } + + renderer.RedrawSurface(); + + renderer.Present(); + } + + // This highlights a new item + protected void Highlight(Sector s) + { + // Redraw lines + if(renderer.StartPlotter(false)) + { + // Undraw previous highlight + if((highlighted != null) && !highlighted.IsDisposed) + renderer.PlotSector(highlighted); + + // Set new highlight + highlighted = s; + + // Get label positions + if(s != null) labelpos = Tools.FindLabelPositions(s); + + // Render highlighted item + if((highlighted != null) && !highlighted.IsDisposed) + renderer.PlotSector(highlighted, General.Colors.Highlight); + + // Done + renderer.Finish(); + } + + // Render labels + /* + if(renderer.StartOverlay(true)) + { + if(labelpos != null) + { + foreach(LabelPositionInfo lb in labelpos) + renderer.RenderRectangleFilled(new RectangleF(lb.position.x - lb.radius / 2, lb.position.y - lb.radius / 2, lb.radius, lb.radius), General.Colors.Indication, true); + } + + renderer.Finish(); + } + */ + + renderer.Present(); + + // Show highlight info + if((highlighted != null) && !highlighted.IsDisposed) + General.Interface.ShowSectorInfo(highlighted); + else + General.Interface.HideInfo(); + } + + // Mouse moves + public override void OnMouseMove(MouseEventArgs e) + { + base.OnMouseMove(e); + + // Find the nearest linedef within highlight range + Linedef l = General.Map.Map.NearestLinedef(mousemappos); + + // Check on which side of the linedef the mouse is + float side = l.SideOfLine(mousemappos); + if(side > 0) + { + // Is there a sidedef here? + if(l.Back != null) + { + // Highlight if not the same + if(l.Back.Sector != highlighted) Highlight(l.Back.Sector); + } + else + { + // Highlight nothing + if(highlighted != null) Highlight(null); + } + } + else + { + // Is there a sidedef here? + if(l.Front != null) + { + // Highlight if not the same + if(l.Front.Sector != highlighted) Highlight(l.Front.Sector); + } + else + { + // Highlight nothing + if(highlighted != null) Highlight(null); + } + } + } + + // Mouse leaves + public override void OnMouseLeave(EventArgs e) + { + base.OnMouseLeave(e); + + // Highlight nothing + Highlight(null); + } + + // Mouse button pressed + public override void OnMouseDown(MouseEventArgs e) + { + base.OnMouseDown(e); + bool front, back; + + // Edit button is used? + if(General.Actions.CheckActionActive(null, "classicedit")) + { + // Item highlighted? + if((highlighted != null) && !highlighted.IsDisposed) + { + // Make update lines selection + foreach(Sidedef sd in highlighted.Sidedefs) + { + if(sd.Line.Front != null) front = sd.Line.Front.Sector.Selected; else front = false; + if(sd.Line.Back != null) back = sd.Line.Back.Sector.Selected; else back = false; + sd.Line.Selected = front | back; + } + + // Update display + if(renderer.StartPlotter(false)) + { + // Redraw highlight to show selection + renderer.PlotSector(highlighted); + renderer.Finish(); + renderer.Present(); + } + } + } + } + + // Mouse released + public override void OnMouseUp(MouseEventArgs e) + { + ICollection selected; + PixelColor c; + + base.OnMouseUp(e); + + // Item highlighted? + if((highlighted != null) && !highlighted.IsDisposed) + { + Sector s = highlighted; + + // Remove highlight to avoid confusion + Highlight(null); + + // Get a triangulator and bind events + Triangulation t = new Triangulation(); + t.OnShowPolygon = ShowPolygon; + t.OnShowEarClip = ShowEarClip; + //t.OnShowRemaining = ShowRemaining; + t.Triangulate(s); + + // Start with a clear display + if(renderer.StartPlotter(true)) + { + // Render lines and vertices + renderer.PlotLinedefSet(General.Map.Map.Linedefs); + renderer.PlotVerticesSet(General.Map.Map.Vertices); + + // Go for all triangle vertices to render the inside lines only + for(int i = 0; i < t.Vertices.Count; i += 3) + { + if(t.Sidedefs[i + 0] == null) renderer.PlotLine(t.Vertices[i + 0], t.Vertices[i + 1], PixelColor.FromColor(Color.DeepSkyBlue)); + if(t.Sidedefs[i + 1] == null) renderer.PlotLine(t.Vertices[i + 1], t.Vertices[i + 2], PixelColor.FromColor(Color.DeepSkyBlue)); + if(t.Sidedefs[i + 2] == null) renderer.PlotLine(t.Vertices[i + 2], t.Vertices[i + 0], PixelColor.FromColor(Color.DeepSkyBlue)); + } + + // Go for all triangle vertices to renderthe outside lines only + for(int i = 0; i < t.Vertices.Count; i += 3) + { + if(t.Sidedefs[i + 0] != null) renderer.PlotLine(t.Vertices[i + 0], t.Vertices[i + 1], PixelColor.FromColor(Color.Red)); + if(t.Sidedefs[i + 1] != null) renderer.PlotLine(t.Vertices[i + 1], t.Vertices[i + 2], PixelColor.FromColor(Color.Red)); + if(t.Sidedefs[i + 2] != null) renderer.PlotLine(t.Vertices[i + 2], t.Vertices[i + 0], PixelColor.FromColor(Color.Red)); + } + + // Done + renderer.Finish(); + renderer.Present(); + Thread.Sleep(200); + } + } + } + + // This shows a point + private void ShowPoint(Vector2D v, int c) + { + for(int a = 0; a < 6; a++) + { + OnRedrawDisplay(); + Thread.Sleep(20); + Application.DoEvents(); + + // Start with a clear display + if(renderer.StartPlotter(true)) + { + // Render lines and vertices + renderer.PlotLinedefSet(General.Map.Map.Linedefs); + renderer.PlotVerticesSet(General.Map.Map.Vertices); + + // Show the point + renderer.PlotVertexAt(v, c); + + // Done + renderer.Finish(); + renderer.Present(); + } + + // Wait a bit + Thread.Sleep(60); + } + } + + // This shows a line + private void ShowLine(Vector2D v1, Vector2D v2, PixelColor c) + { + for(int a = 0; a < 3; a++) + { + OnRedrawDisplay(); + Thread.Sleep(20); + Application.DoEvents(); + + // Start with a clear display + if(renderer.StartPlotter(true)) + { + // Render lines and vertices + renderer.PlotLinedefSet(General.Map.Map.Linedefs); + renderer.PlotVerticesSet(General.Map.Map.Vertices); + + // Show the line + renderer.PlotLine(v1, v2, c); + + // Done + renderer.Finish(); + renderer.Present(); + } + + // Wait a bit + Thread.Sleep(50); + Application.DoEvents(); + } + } + + // This shows a polygon + private void ShowPolygon(LinkedList p) + { + LinkedListNode v = p.First; + LinkedListNode v2 = p.Last; + + // Go for all vertices in the polygon + while(v != null) + { + for(int a = 0; a < 1; a++) + { + + // Start with a clear display + if(renderer.StartPlotter(true)) + { + // Render lines and vertices + renderer.PlotLinedefSet(General.Map.Map.Linedefs); + renderer.PlotVerticesSet(General.Map.Map.Vertices); + + // Show line + renderer.PlotLine(v.Value.Position, v2.Value.Position, PixelColor.FromColor(Color.White)); + + // Done + renderer.Finish(); + renderer.Present(); + } + + Thread.Sleep(10); + + // Start with a clear display + if(renderer.StartPlotter(true)) + { + // Render lines and vertices + renderer.PlotLinedefSet(General.Map.Map.Linedefs); + renderer.PlotVerticesSet(General.Map.Map.Vertices); + + // Show line + renderer.PlotLine(v.Value.Position, v2.Value.Position, PixelColor.FromColor(Color.Red)); + + // Done + renderer.Finish(); + renderer.Present(); + } + + // Wait a bit + Thread.Sleep(40); + Application.DoEvents(); + } + + v2 = v; + v = v.Next; + } + } + + // This shows a polygon + private void ShowRemaining(LinkedList remains) + { + LinkedListNode v; + + for(int a = 0; a < 100; a++) + { + OnRedrawDisplay(); + Thread.Sleep(10); + Application.DoEvents(); + + // Start with a clear display + if(renderer.StartPlotter(true)) + { + // Render lines and vertices + renderer.PlotLinedefSet(General.Map.Map.Linedefs); + renderer.PlotVerticesSet(General.Map.Map.Vertices); + + // Go for all vertices in the polygon + v = remains.First; + while(v != null) + { + // Show the line + if(v.Next != null) renderer.PlotLine(v.Value.Position, v.Next.Value.Position, PixelColor.FromColor(Color.Yellow)); + v = v.Next; + } + + // Show last line as well + renderer.PlotLine(remains.Last.Value.Position, remains.First.Value.Position, PixelColor.FromColor(Color.Yellow)); + + // Done + renderer.Finish(); + renderer.Present(); + } + + // Wait a bit + Thread.Sleep(60); + } + } + + // This shows a polygon + private void ShowEarClip(EarClipVertex[] found, LinkedList remains) + { + EarClipVertex prev, first; + + for(int a = 0; a < 8; a++) + { + // Start with a clear display + if(renderer.StartPlotter(true)) + { + // Render lines and vertices + renderer.PlotLinedefSet(General.Map.Map.Linedefs); + renderer.PlotVerticesSet(General.Map.Map.Vertices); + + // Go for all remaining vertices + prev = null; first = null; + foreach(EarClipVertex v in remains) + { + // Show the line + if(prev != null) renderer.PlotLine(v.Position, prev.Position, PixelColor.FromColor(Color.Yellow)); + if(prev == null) first = v; + prev = v; + + if(v.IsReflex) + renderer.PlotVertexAt(v.Position, ColorCollection.SELECTION); + else + renderer.PlotVertexAt(v.Position, ColorCollection.VERTICES); + } + if(first != null) renderer.PlotLine(first.Position, prev.Position, PixelColor.FromColor(Color.Yellow)); + + if(found != null) + { + renderer.PlotLine(found[0].Position, found[1].Position, PixelColor.FromColor(Color.BlueViolet)); + renderer.PlotLine(found[1].Position, found[2].Position, PixelColor.FromColor(Color.BlueViolet)); + renderer.PlotLine(found[2].Position, found[0].Position, PixelColor.FromColor(Color.BlueViolet)); + renderer.PlotVertexAt(found[1].Position, ColorCollection.INDICATION); + } + + // Done + renderer.Finish(); + renderer.Present(); + } + Thread.Sleep(60); + Application.DoEvents(); + + // Start with a clear display + if(renderer.StartPlotter(true)) + { + // Render lines and vertices + renderer.PlotLinedefSet(General.Map.Map.Linedefs); + renderer.PlotVerticesSet(General.Map.Map.Vertices); + + // Go for all remaining vertices + prev = null; first = null; + foreach(EarClipVertex v in remains) + { + // Show the line + if(prev != null) renderer.PlotLine(v.Position, prev.Position, PixelColor.FromColor(Color.Yellow)); + if(prev == null) first = v; + prev = v; + + if(v.IsReflex) + renderer.PlotVertexAt(v.Position, ColorCollection.SELECTION); + else + renderer.PlotVertexAt(v.Position, ColorCollection.VERTICES); + } + if(first != null) renderer.PlotLine(first.Position, prev.Position, PixelColor.FromColor(Color.Yellow)); + + // Done + renderer.Finish(); + renderer.Present(); + } + Thread.Sleep(40); + } + } + + #endregion + } + + #endif +} diff --git a/Source/Plugins/BuilderModes/ClassicModes/VerticesMode.cs b/Source/Plugins/BuilderModes/ClassicModes/VerticesMode.cs new file mode 100644 index 0000000..24b1fa8 --- /dev/null +++ b/Source/Plugins/BuilderModes/ClassicModes/VerticesMode.cs @@ -0,0 +1,721 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using CodeImp.DoomBuilder.Windows; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Geometry; +using CodeImp.DoomBuilder.Editing; +using CodeImp.DoomBuilder.Actions; + +#endregion + +namespace CodeImp.DoomBuilder.BuilderModes +{ + [EditMode(DisplayName = "Vertices Mode", + SwitchAction = "verticesmode", // Action name used to switch to this mode + ButtonImage = "VerticesMode.png", // Image resource name for the button + ButtonOrder = int.MinValue + 0, // Position of the button (lower is more to the left) + ButtonGroup = "000_editing", + UseByDefault = true, + SafeStartMode = true)] + + public class VerticesMode : BaseClassicMode + { + #region ================== Constants + + #endregion + + #region ================== Variables + + // Highlighted item + protected Vertex highlighted; + + // Interface + private bool editpressed; + + #endregion + + #region ================== Properties + + public override object HighlightedObject { get { return highlighted; } } + + #endregion + + #region ================== Constructor / Disposer + + #endregion + + #region ================== Methods + + public override void OnHelp() + { + General.ShowHelp("e_vertices.html"); + } + + // Cancel mode + public override void OnCancel() + { + base.OnCancel(); + + // Return to this mode + General.Editing.ChangeMode(new VerticesMode()); + } + + // Mode engages + public override void OnEngage() + { + base.OnEngage(); + renderer.SetPresentation(Presentation.Standard); + + // Add toolbar buttons + General.Interface.AddButton(BuilderPlug.Me.MenusForm.CopyProperties); + General.Interface.AddButton(BuilderPlug.Me.MenusForm.PasteProperties); + + // Convert geometry selection to vertices only + General.Map.Map.ConvertSelection(SelectionType.Vertices); + } + + // Mode disengages + public override void OnDisengage() + { + base.OnDisengage(); + + // Remove toolbar buttons + General.Interface.RemoveButton(BuilderPlug.Me.MenusForm.CopyProperties); + General.Interface.RemoveButton(BuilderPlug.Me.MenusForm.PasteProperties); + + // Going to EditSelectionMode? + if (General.Editing.NewMode is EditSelectionMode) + { + // Not pasting anything? + EditSelectionMode editmode = (General.Editing.NewMode as EditSelectionMode); + if (!editmode.Pasting) + { + // No selection made? But we have a highlight! + if ((General.Map.Map.GetSelectedVertices(true).Count == 0) && (highlighted != null)) + { + // Make the highlight the selection + highlighted.Selected = true; + } + } + } + + // Hide highlight info + General.Interface.HideInfo(); + } + + // This redraws the display + public override void OnRedrawDisplay() + { + renderer.RedrawSurface(); + + // Render lines and vertices + if (renderer.StartPlotter(true)) + { + renderer.PlotLinedefSet(General.Map.Map.Linedefs); + renderer.PlotVerticesSet(General.Map.Map.Vertices); + if ((highlighted != null) && !highlighted.IsDisposed) + renderer.PlotVertex(highlighted, ColorCollection.HIGHLIGHT); + renderer.Finish(); + } + + // Render things + if (renderer.StartThings(true)) + { + renderer.RenderThingSet(General.Map.ThingsFilter.HiddenThings, Presentation.THINGS_HIDDEN_ALPHA); + renderer.RenderThingSet(General.Map.ThingsFilter.VisibleThings, 1.0f); + renderer.Finish(); + } + + // Selecting? + if (selecting) + { + // Render selection + if (renderer.StartOverlay(true)) + { + RenderMultiSelection(); + renderer.Finish(); + } + } + + renderer.Present(); + } + + // This highlights a new item + protected void Highlight(Vertex v) + { + // Update display + if (renderer.StartPlotter(false)) + { + // Undraw previous highlight + if ((highlighted != null) && !highlighted.IsDisposed) + renderer.PlotVertex(highlighted, renderer.DetermineVertexColor(highlighted)); + + // Set new highlight + highlighted = v; + + // Render highlighted item + if ((highlighted != null) && !highlighted.IsDisposed) + renderer.PlotVertex(highlighted, ColorCollection.HIGHLIGHT); + + // Done + renderer.Finish(); + renderer.Present(); + } + + // Show highlight info + if ((highlighted != null) && !highlighted.IsDisposed) + General.Interface.ShowVertexInfo(highlighted); + else + General.Interface.HideInfo(); + } + + // Selection + protected override void OnSelectBegin() + { + // Item highlighted? + if ((highlighted != null) && !highlighted.IsDisposed) + { + // Flip selection + highlighted.Selected = !highlighted.Selected; + + // Redraw highlight to show selection + if (renderer.StartPlotter(false)) + { + renderer.PlotVertex(highlighted, renderer.DetermineVertexColor(highlighted)); + renderer.Finish(); + renderer.Present(); + } + } + else + { + // Start making a selection + StartMultiSelection(); + } + + base.OnSelectBegin(); + } + + // End selection + protected override void OnSelectEnd() + { + // Not stopping from multiselection? + if (!selecting) + { + // Item highlighted? + if ((highlighted != null) && !highlighted.IsDisposed) + { + // Render highlighted item + if (renderer.StartPlotter(false)) + { + renderer.PlotVertex(highlighted, ColorCollection.HIGHLIGHT); + renderer.Finish(); + renderer.Present(); + } + } + } + + base.OnSelectEnd(); + } + + // Start editing + protected override void OnEditBegin() + { + bool snaptogrid = General.Interface.ShiftState ^ General.Interface.SnapToGrid; + bool snaptonearest = General.Interface.CtrlState ^ General.Interface.AutoMerge; + + // Vertex highlighted? + if ((highlighted != null) && !highlighted.IsDisposed) + { + // Edit pressed in this mode + editpressed = true; + + // Highlighted item not selected? + if (!highlighted.Selected && (BuilderPlug.Me.AutoClearSelection || (General.Map.Map.SelectedVerticessCount == 0))) + { + // Make this the only selection + General.Map.Map.ClearSelectedVertices(); + highlighted.Selected = true; + General.Interface.RedrawDisplay(); + } + + // Update display + if (renderer.StartPlotter(false)) + { + // Redraw highlight to show selection + renderer.PlotVertex(highlighted, renderer.DetermineVertexColor(highlighted)); + renderer.Finish(); + renderer.Present(); + } + } + else + { + // Find the nearest linedef within highlight range + Linedef l = General.Map.Map.NearestLinedefRange(mousemappos, BuilderPlug.Me.SplitLinedefsRange / renderer.Scale); + if (l != null) + { + // Create undo + General.Map.UndoRedo.CreateUndo("Split linedef"); + + Vector2D insertpos; + + // Snip to grid also? + if (snaptogrid) + { + // Find all points where the grid intersects the line + List points = l.GetGridIntersections(); + insertpos = mousemappos; + float distance = float.MaxValue; + foreach (Vector2D p in points) + { + float pdist = Vector2D.DistanceSq(p, mousemappos); + if (pdist < distance) + { + insertpos = p; + distance = pdist; + } + } + } + else + { + // Just use the nearest point on line + insertpos = l.NearestOnLine(mousemappos); + } + + // Make the vertex + Vertex v = General.Map.Map.CreateVertex(insertpos); + if (v == null) + { + General.Map.UndoRedo.WithdrawUndo(); + return; + } + + // Snap to map format accuracy + v.SnapToAccuracy(); + + // Split the line with this vertex + Linedef sld = l.Split(v); + if (sld == null) + { + General.Map.UndoRedo.WithdrawUndo(); + return; + } + + // Update + General.Map.Map.Update(); + + // Highlight it + Highlight(v); + + // Redraw display + General.Interface.RedrawDisplay(); + } + else + { + // Start drawing mode + DrawGeometryMode drawmode = new DrawGeometryMode(); + DrawnVertex v = DrawGeometryMode.GetCurrentPosition(mousemappos, snaptonearest, snaptogrid, renderer, new List()); + + if (drawmode.DrawPointAt(v)) + General.Editing.ChangeMode(drawmode); + else + General.Interface.DisplayStatus(StatusType.Warning, "Failed to draw point: outside of map boundaries."); + } + } + + base.OnEditBegin(); + } + + // Done editing + protected override void OnEditEnd() + { + // Edit pressed in this mode? + if (editpressed) + { + // Anything selected? + ICollection selected = General.Map.Map.GetSelectedVertices(true); + if (selected.Count > 0) + { + if (General.Interface.IsActiveWindow) + { + // Show line edit dialog + General.Interface.ShowEditVertices(selected); + General.Map.Map.Update(); + + // When a single vertex was selected, deselect it now + if (selected.Count == 1) General.Map.Map.ClearSelectedVertices(); + + // Update entire display + General.Interface.RedrawDisplay(); + } + } + } + + editpressed = false; + base.OnEditEnd(); + } + + // Mouse moves + public override void OnMouseMove(MouseEventArgs e) + { + base.OnMouseMove(e); + + // Not holding any buttons? + if (e.Button == MouseButtons.None) + { + // Find the nearest vertex within highlight range + Vertex v = General.Map.Map.NearestVertexSquareRange(mousemappos, BuilderPlug.Me.HighlightRange / renderer.Scale); + + // Highlight if not the same + if (v != highlighted) Highlight(v); + } + } + + // Mouse leaves + public override void OnMouseLeave(EventArgs e) + { + base.OnMouseLeave(e); + + // Highlight nothing + Highlight(null); + } + + // Mouse wants to drag + protected override void OnDragStart(MouseEventArgs e) + { + base.OnDragStart(e); + + // Edit button used? + if (General.Actions.CheckActionActive(null, "classicedit")) + { + // Anything highlighted? + if ((highlighted != null) && !highlighted.IsDisposed) + { + // Highlighted item not selected? + if (!highlighted.Selected) + { + // Select only this vertex for dragging + General.Map.Map.ClearSelectedVertices(); + highlighted.Selected = true; + } + + // Start dragging the selection + General.Editing.ChangeMode(new DragVerticesMode(highlighted, mousedownmappos)); + } + } + } + + // This is called wheh selection ends + protected override void OnEndMultiSelection() + { + bool selectionvolume = ((Math.Abs(base.selectionrect.Width) > 0.1f) && (Math.Abs(base.selectionrect.Height) > 0.1f)); + + if (BuilderPlug.Me.AutoClearSelection && !selectionvolume) + General.Map.Map.ClearSelectedVertices(); + + if (selectionvolume) + { + if (General.Interface.ShiftState ^ BuilderPlug.Me.AdditiveSelect) + { + // Go for all vertices + foreach (Vertex v in General.Map.Map.Vertices) + { + v.Selected |= ((v.Position.x >= selectionrect.Left) && + (v.Position.y >= selectionrect.Top) && + (v.Position.x <= selectionrect.Right) && + (v.Position.y <= selectionrect.Bottom)); + } + } + else + { + // Go for all vertices + foreach (Vertex v in General.Map.Map.Vertices) + { + v.Selected = ((v.Position.x >= selectionrect.Left) && + (v.Position.y >= selectionrect.Top) && + (v.Position.x <= selectionrect.Right) && + (v.Position.y <= selectionrect.Bottom)); + } + } + } + + base.OnEndMultiSelection(); + + // Clear overlay + if (renderer.StartOverlay(true)) renderer.Finish(); + + // Redraw + General.Interface.RedrawDisplay(); + } + + // This is called when the selection is updated + protected override void OnUpdateMultiSelection() + { + base.OnUpdateMultiSelection(); + + // Render selection + if (renderer.StartOverlay(true)) + { + RenderMultiSelection(); + renderer.Finish(); + renderer.Present(); + } + } + + // When copying + public override bool OnCopyBegin() + { + // No selection made? But we have a highlight! + if ((General.Map.Map.GetSelectedVertices(true).Count == 0) && (highlighted != null)) + { + // Make the highlight the selection + highlighted.Selected = true; + } + + return base.OnCopyBegin(); + } + + #endregion + + #region ================== Actions + + // This copies the properties + [BeginAction("classiccopyproperties")] + public void CopyProperties() + { + // Determine source vertices + ICollection sel = null; + if (General.Map.Map.SelectedVerticessCount > 0) + sel = General.Map.Map.GetSelectedVertices(true); + else if (highlighted != null) + { + sel = new List(); + sel.Add(highlighted); + } + + if (sel != null) + { + // Copy properties from first source vertex + BuilderPlug.Me.CopiedVertexProps = new VertexProperties(General.GetByIndex(sel, 0)); + General.Interface.DisplayStatus(StatusType.Action, "Copied vertex properties."); + } + } + + // This pastes the properties + [BeginAction("classicpasteproperties")] + public void PasteProperties() + { + if (BuilderPlug.Me.CopiedVertexProps != null) + { + // Determine target vertices + ICollection sel = null; + if (General.Map.Map.SelectedVerticessCount > 0) + sel = General.Map.Map.GetSelectedVertices(true); + else if (highlighted != null) + { + sel = new List(); + sel.Add(highlighted); + } + + if (sel != null) + { + // Apply properties to selection + General.Map.UndoRedo.CreateUndo("Paste vertex properties"); + foreach (Vertex v in sel) + { + BuilderPlug.Me.CopiedVertexProps.Apply(v); + } + General.Interface.DisplayStatus(StatusType.Action, "Pasted vertex properties."); + + // Update and redraw + General.Map.IsChanged = true; + General.Interface.RefreshInfo(); + General.Interface.RedrawDisplay(); + } + } + } + + // This clears the selection + [BeginAction("clearselection", BaseAction = true)] + public void ClearSelection() + { + // Clear selection + General.Map.Map.ClearAllSelected(); + + // Redraw + General.Interface.RedrawDisplay(); + } + + // This creates a new vertex at the mouse position + [BeginAction("insertitem", BaseAction = true)] + public virtual void InsertVertexAction() { VerticesMode.InsertVertex(mousemappos, renderer.Scale); } + public static void InsertVertex(Vector2D mousemappos, float rendererscale) + { + bool snaptogrid = General.Interface.ShiftState ^ General.Interface.SnapToGrid; + bool snaptonearest = General.Interface.CtrlState ^ General.Interface.AutoMerge; + + // Mouse in window? + if (General.Interface.MouseInDisplay) + { + Vector2D insertpos; + Linedef l = null; + + // Create undo + General.Map.UndoRedo.CreateUndo("Insert vertex"); + + // Snap to geometry? + l = General.Map.Map.NearestLinedefRange(mousemappos, BuilderPlug.Me.SplitLinedefsRange / rendererscale); + if (snaptonearest && (l != null)) + { + // Snip to grid also? + if (snaptogrid) + { + // Find all points where the grid intersects the line + List points = l.GetGridIntersections(); + insertpos = mousemappos; + float distance = float.MaxValue; + foreach (Vector2D p in points) + { + float pdist = Vector2D.DistanceSq(p, mousemappos); + if (pdist < distance) + { + insertpos = p; + distance = pdist; + } + } + } + else + { + // Just use the nearest point on line + insertpos = l.NearestOnLine(mousemappos); + } + } + // Snap to grid? + else if (snaptogrid) + { + // Snap to grid + insertpos = General.Map.Grid.SnappedToGrid(mousemappos); + } + else + { + // Just insert here, don't snap to anything + insertpos = mousemappos; + } + + // Make the vertex + Vertex v = General.Map.Map.CreateVertex(insertpos); + if (v == null) + { + General.Map.UndoRedo.WithdrawUndo(); + return; + } + + // Snap to map format accuracy + v.SnapToAccuracy(); + + // Split the line with this vertex + if (snaptonearest && (l != null)) + { + General.Interface.DisplayStatus(StatusType.Action, "Split a linedef."); + l.Split(v); + } + else + { + General.Interface.DisplayStatus(StatusType.Action, "Inserted a vertex."); + } + + // Update + General.Map.Map.Update(); + + // Redraw screen + General.Interface.RedrawDisplay(); + } + } + + [BeginAction("deleteitem", BaseAction = true)] + public void DeleteItem() + { + // Make list of selected vertices + ICollection selected = General.Map.Map.GetSelectedVertices(true); + if ((selected.Count == 0) && (highlighted != null) && !highlighted.IsDisposed) selected.Add(highlighted); + + // Anything to do? + if (selected.Count > 0) + { + // Make undo + if (selected.Count > 1) + { + General.Map.UndoRedo.CreateUndo("Delete " + selected.Count + " vertices"); + General.Interface.DisplayStatus(StatusType.Action, "Deleted " + selected.Count + " vertices."); + } + else + { + General.Map.UndoRedo.CreateUndo("Delete vertex"); + General.Interface.DisplayStatus(StatusType.Action, "Deleted a vertex."); + } + + // Go for all vertices that need to be removed + foreach (Vertex v in selected) + { + // Not already removed automatically? + if (!v.IsDisposed) + { + // If the vertex only has 2 linedefs attached, then merge the linedefs + if (v.Linedefs.Count == 2) + { + Linedef ld1 = General.GetByIndex(v.Linedefs, 0); + Linedef ld2 = General.GetByIndex(v.Linedefs, 1); + Vertex v1 = (ld1.Start == v) ? ld1.End : ld1.Start; + Vertex v2 = (ld2.Start == v) ? ld2.End : ld2.Start; + if (ld1.Start == v) ld1.SetStartVertex(v2); else ld1.SetEndVertex(v2); + //if(ld2.Start == v) ld2.SetStartVertex(v1); else ld2.SetEndVertex(v1); + //ld1.Join(ld2); + ld2.Dispose(); + } + + // Trash vertex + v.Dispose(); + } + } + + // Update cache values + General.Map.IsChanged = true; + General.Map.Map.Update(); + + // Invoke a new mousemove so that the highlighted item updates + MouseEventArgs e = new MouseEventArgs(MouseButtons.None, 0, (int)mousepos.x, (int)mousepos.y, 0); + OnMouseMove(e); + + // Redraw screen + General.Interface.RedrawDisplay(); + } + } + + #endregion + } +} diff --git a/Source/Plugins/BuilderModes/ClassicModes/WAuthorMode.cs b/Source/Plugins/BuilderModes/ClassicModes/WAuthorMode.cs new file mode 100644 index 0000000..9a58f75 --- /dev/null +++ b/Source/Plugins/BuilderModes/ClassicModes/WAuthorMode.cs @@ -0,0 +1,392 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using CodeImp.DoomBuilder.Windows; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Geometry; +using CodeImp.DoomBuilder.Editing; +using CodeImp.DoomBuilder.Actions; + +#endregion + +namespace CodeImp.DoomBuilder.BuilderModes.Editing +{ + #if DEBUG + + [EditMode(DisplayName = "WadAuthor Mode", + SwitchAction = "wauthormode", + ButtonImage = "WAuthor.png", + ButtonOrder = int.MinValue + 4)] + + public class WAuthorMode : ClassicMode + { + #region ================== Constants + + protected const float LINEDEF_HIGHLIGHT_RANGE = 10f; + protected const float VERTEX_HIGHLIGHT_RANGE = 8f; + protected const float THING_HIGHLIGHT_RANGE = 2f; + + #endregion + + #region ================== Variables + + // Tools + protected WAuthorTools tools; + + // Highlighted item + protected object highlighted; + + #endregion + + #region ================== Properties + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + public WAuthorMode() + { + // Initialize + tools = new WAuthorTools(); + + // Enable this and you'll have a floating window + //tools.Show(General.Interface); + //General.Interface.Focus(); + + // We have no destructor + GC.SuppressFinalize(this); + } + + // Disposer + public override void Dispose() + { + // Not already disposed? + if(!isdisposed) + { + // Clean up + tools.Dispose(); + + // Dispose base + base.Dispose(); + } + } + + #endregion + + #region ================== Methods + + // Cancel mode + public override void OnCancel() + { + base.OnCancel(); + + // Return to this mode + General.Editing.ChangeMode(new WAuthorMode()); + } + + // Mode engages + public override void OnEngage() + { + base.OnEngage(); + } + + // Mode disengages + public override void OnDisengage() + { + base.OnDisengage(); + + // Clear selected vertices + General.Map.Map.ClearAllSelected(); + + // Hide highlight info + General.Interface.HideInfo(); + } + + // This redraws the display + public unsafe override void OnRedrawDisplay() + { + // Render lines and vertices + if(renderer.StartPlotter(true)) + { + renderer.PlotLinedefSet(General.Map.Map.Linedefs); + renderer.PlotVerticesSet(General.Map.Map.Vertices); + if((highlighted != null) && !(highlighted is Thing)) DrawHighlight(true); + renderer.Finish(); + } + + // Render things + if(renderer.StartThings(true)) + { + renderer.RenderThingSet(General.Map.Map.Things, 1.0f); + if((highlighted != null) && (highlighted is Thing)) DrawHighlight(true); + renderer.Finish(); + } + + renderer.Present(); + } + + // This draws the highlighted item + protected void DrawHighlight(bool highlightcolor) + { + // With highlight color + if(highlightcolor) + { + // Vertex + if(highlighted is Vertex) + { + if((highlighted as Vertex).IsDisposed) return; + renderer.PlotVertex(highlighted as Vertex, ColorCollection.HIGHLIGHT); + } + // Linedef + else if(highlighted is Linedef) + { + if((highlighted as Linedef).IsDisposed) return; + renderer.PlotLinedef((highlighted as Linedef), General.Colors.Highlight); + renderer.PlotVertex((highlighted as Linedef).Start, renderer.DetermineVertexColor((highlighted as Linedef).Start)); + renderer.PlotVertex((highlighted as Linedef).End, renderer.DetermineVertexColor((highlighted as Linedef).End)); + } + // Sector + else if(highlighted is Sector) + { + if((highlighted as Sector).IsDisposed) return; + renderer.PlotSector((highlighted as Sector), General.Colors.Highlight); + } + // Thing + else if(highlighted is Thing) + { + if((highlighted as Thing).IsDisposed) return; + renderer.RenderThing((highlighted as Thing), General.Colors.Highlight, 1.0f); + } + } + // With original color + else + { + // Vertex + if(highlighted is Vertex) + { + if((highlighted as Vertex).IsDisposed) return; + renderer.PlotVertex(highlighted as Vertex, renderer.DetermineVertexColor(highlighted as Vertex)); + } + // Linedef + else if(highlighted is Linedef) + { + if((highlighted as Linedef).IsDisposed) return; + renderer.PlotLinedef((highlighted as Linedef), renderer.DetermineLinedefColor((highlighted as Linedef))); + renderer.PlotVertex((highlighted as Linedef).Start, renderer.DetermineVertexColor((highlighted as Linedef).Start)); + renderer.PlotVertex((highlighted as Linedef).End, renderer.DetermineVertexColor((highlighted as Linedef).End)); + } + // Sector + else if(highlighted is Sector) + { + if((highlighted as Sector).IsDisposed) return; + renderer.PlotSector((highlighted as Sector)); + } + // Thing + else if(highlighted is Thing) + { + if((highlighted as Thing).IsDisposed) return; + renderer.RenderThing((highlighted as Thing), renderer.DetermineThingColor((highlighted as Thing)), 1.0f); + } + } + } + + // This highlights a new item + protected void Highlight(object h) + { + bool renderresult; + + // Changes? + if(highlighted != h) + { + if(highlighted != null) + { + // Start update + if(highlighted is Thing) + renderresult = renderer.StartThings(false); + else + renderresult = renderer.StartPlotter(false); + + // Undraw previous highlight + if(renderresult) + { + DrawHighlight(false); + renderer.Finish(); + } + } + + // Set new highlight + highlighted = h; + + if(highlighted != null) + { + // Start update + if(highlighted is Thing) + renderresult = renderer.StartThings(false); + else + renderresult = renderer.StartPlotter(false); + + // Undraw previous highlight + if(renderresult) + { + DrawHighlight(true); + renderer.Finish(); + } + } + + // Hide info + General.Interface.HideInfo(); + + // Anything highlighted? + if(highlighted != null) + { + // Show highlight info + if(highlighted is Vertex) + General.Interface.ShowVertexInfo(highlighted as Vertex); + else if(highlighted is Linedef) + General.Interface.ShowLinedefInfo(highlighted as Linedef); + else if(highlighted is Sector) + General.Interface.ShowSectorInfo(highlighted as Sector); + else if(highlighted is Thing) + General.Interface.ShowThingInfo(highlighted as Thing); + } + + renderer.Present(); + } + } + + // Mouse moves + public override void OnMouseMove(MouseEventArgs e) + { + base.OnMouseMove(e); + + // Not holding any buttons? + if(e.Button == MouseButtons.None) + { + // Find the nearest items within highlight range + Vertex v = General.Map.Map.NearestVertexSquareRange(mousemappos, VERTEX_HIGHLIGHT_RANGE / renderer.Scale); + Thing t = General.Map.Map.NearestThingSquareRange(mousemappos, THING_HIGHLIGHT_RANGE / renderer.Scale); + Linedef l = General.Map.Map.NearestLinedef(mousemappos); + Sector s; + + // Check on which side of the linedef the mouse is + float side = l.SideOfLine(mousemappos); + if(side > 0) + { + // Is there a sidedef here? + if(l.Back != null) s = l.Back.Sector; + else s = null; + } + else + { + // Is there a sidedef here? + if(l.Front != null) s = l.Front.Sector; + else s = null; + } + + // Both a vertex and thing in range? + if((v != null) && (t != null)) + { + // Highlight closest + float vd = v.DistanceToSq(mousemappos); + float td = t.DistanceToSq(mousemappos); + if(vd < td) Highlight(v); else Highlight(t); + } + // Vertex in range? + else if(v != null) + { + // Highlight vertex + Highlight(v); + } + // Thing in range? + else if(t != null) + { + // Highlight thing + Highlight(t); + } + else + { + // Linedef within in range? + float ld = l.DistanceTo(mousemappos, true); + if(ld < (LINEDEF_HIGHLIGHT_RANGE / renderer.Scale)) + { + // Highlight line + Highlight(l); + } + // Mouse inside a sector? + else if(s != null) + { + // Highlight sector + Highlight(s); + } + else + { + // Highlight nothing + Highlight(null); + } + } + } + } + + // Maybe i'll finish this later, or not even include this mode at all, not sure yet. + + // Mouse leaves + public override void OnMouseLeave(EventArgs e) + { + base.OnMouseLeave(e); + + // Highlight nothing + Highlight(null); + } + + // Mouse button pressed + public override void OnMouseDown(MouseEventArgs e) + { + base.OnMouseDown(e); + } + + // Mouse released + public override void OnMouseUp(MouseEventArgs e) + { + base.OnMouseUp(e); + + // This shows a popup menu + tools.LinedefPopup.Show(Cursor.Position); + } + + // Mouse wants to drag + protected override void OnDragStart(MouseEventArgs e) + { + base.OnDragStart(e); + } + + #endregion + } + + #endif +} diff --git a/Source/Plugins/BuilderModes/ClassicModes/WAuthorTools.Designer.cs b/Source/Plugins/BuilderModes/ClassicModes/WAuthorTools.Designer.cs new file mode 100644 index 0000000..9559d70 --- /dev/null +++ b/Source/Plugins/BuilderModes/ClassicModes/WAuthorTools.Designer.cs @@ -0,0 +1,116 @@ +namespace CodeImp.DoomBuilder.BuilderModes.Editing +{ + partial class WAuthorTools + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if(disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + this.linedefpopup = new System.Windows.Forms.ContextMenuStrip(this.components); + this.propertiesToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem1 = new System.Windows.Forms.ToolStripSeparator(); + this.deleteToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.splitToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.flipToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.curveToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.linedefpopup.SuspendLayout(); + this.SuspendLayout(); + // + // linedefpopup + // + this.linedefpopup.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.propertiesToolStripMenuItem, + this.toolStripMenuItem1, + this.deleteToolStripMenuItem, + this.splitToolStripMenuItem, + this.flipToolStripMenuItem, + this.curveToolStripMenuItem}); + this.linedefpopup.Name = "linedefpopup"; + this.linedefpopup.Size = new System.Drawing.Size(147, 120); + // + // propertiesToolStripMenuItem + // + this.propertiesToolStripMenuItem.Name = "propertiesToolStripMenuItem"; + this.propertiesToolStripMenuItem.Size = new System.Drawing.Size(146, 22); + this.propertiesToolStripMenuItem.Text = "Properties..."; + // + // toolStripMenuItem1 + // + this.toolStripMenuItem1.Name = "toolStripMenuItem1"; + this.toolStripMenuItem1.Size = new System.Drawing.Size(143, 6); + // + // deleteToolStripMenuItem + // + this.deleteToolStripMenuItem.Name = "deleteToolStripMenuItem"; + this.deleteToolStripMenuItem.Size = new System.Drawing.Size(146, 22); + this.deleteToolStripMenuItem.Text = "Delete"; + // + // splitToolStripMenuItem + // + this.splitToolStripMenuItem.Name = "splitToolStripMenuItem"; + this.splitToolStripMenuItem.Size = new System.Drawing.Size(146, 22); + this.splitToolStripMenuItem.Text = "Split"; + // + // flipToolStripMenuItem + // + this.flipToolStripMenuItem.Name = "flipToolStripMenuItem"; + this.flipToolStripMenuItem.Size = new System.Drawing.Size(146, 22); + this.flipToolStripMenuItem.Text = "Flip"; + // + // curveToolStripMenuItem + // + this.curveToolStripMenuItem.Name = "curveToolStripMenuItem"; + this.curveToolStripMenuItem.Size = new System.Drawing.Size(146, 22); + this.curveToolStripMenuItem.Text = "Curve..."; + // + // WAuthorTools + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 14F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(243, 130); + this.ControlBox = false; + this.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "WAuthorTools"; + this.Text = "WAuthorTools"; + this.linedefpopup.ResumeLayout(false); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.ContextMenuStrip linedefpopup; + private System.Windows.Forms.ToolStripMenuItem propertiesToolStripMenuItem; + private System.Windows.Forms.ToolStripSeparator toolStripMenuItem1; + private System.Windows.Forms.ToolStripMenuItem deleteToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem splitToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem flipToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem curveToolStripMenuItem; + } +} \ No newline at end of file diff --git a/Source/Plugins/BuilderModes/ClassicModes/WAuthorTools.cs b/Source/Plugins/BuilderModes/ClassicModes/WAuthorTools.cs new file mode 100644 index 0000000..1cc9852 --- /dev/null +++ b/Source/Plugins/BuilderModes/ClassicModes/WAuthorTools.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Text; +using System.Windows.Forms; + +namespace CodeImp.DoomBuilder.BuilderModes.Editing +{ + public partial class WAuthorTools : Form + { + // Tools + public ContextMenuStrip LinedefPopup { get { return linedefpopup; } } + + // Constructor + public WAuthorTools() + { + InitializeComponent(); + } + } +} \ No newline at end of file diff --git a/Source/Plugins/BuilderModes/ClassicModes/WAuthorTools.resx b/Source/Plugins/BuilderModes/ClassicModes/WAuthorTools.resx new file mode 100644 index 0000000..9355b1d --- /dev/null +++ b/Source/Plugins/BuilderModes/ClassicModes/WAuthorTools.resx @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + \ No newline at end of file diff --git a/Source/Plugins/BuilderModes/ErrorChecks/CheckClosedSectors.cs b/Source/Plugins/BuilderModes/ErrorChecks/CheckClosedSectors.cs new file mode 100644 index 0000000..256e4a3 --- /dev/null +++ b/Source/Plugins/BuilderModes/ErrorChecks/CheckClosedSectors.cs @@ -0,0 +1,184 @@ + +#region ================== Copyright (c) 2009 Boris Iwanski + +/* + * Copyright (c) 2009 Boris Iwanski + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using CodeImp.DoomBuilder.Windows; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Geometry; +using CodeImp.DoomBuilder.Editing; +using CodeImp.DoomBuilder.Actions; +using CodeImp.DoomBuilder.Types; +using CodeImp.DoomBuilder.Config; +using System.Threading; + +#endregion + +namespace CodeImp.DoomBuilder.BuilderModes +{ + [ErrorChecker("Check closed sectors", true, 300)] + public class CheckClosedSectors : ErrorChecker + { + #region ================== Constants + + private const int PROGRESS_STEP = 40; + + #endregion + + #region ================== Constructor / Destructor + + // Constructor + public CheckClosedSectors() + { + // Total progress is done when all sectors are checked + SetTotalProgress(General.Map.Map.Sectors.Count / PROGRESS_STEP); + } + + #endregion + + #region ================== Methods + + // This runs the check + public override void Run() + { + int progress = 0; + int stepprogress = 0; + + // This is a simple yet effective way to check if a sector is closed. + // Each sidedef that belongs to a sector is checked out. The lines vertices + // are used as the key in a Dictionary. While going through all the + // sidedefs the values associated to the keys are set with a bitwise OR. + // + // The idea for this method was taken from the way DEU checks for closed + // sectors + // + // The starting vertex of the side gets its 1 bit set, the ending vertex + // of the side gets its 2 bit set. + // The resulting dictionary will have 1, 2 or 3 as values (unlike the DEU + // version 0 will not appear here, since we only look at sides that belong + // to the current sector). + // Values 1 and 2 mean that a line they belong to has no side belonging to + // the current sector, thereby making the sector unclosed. + // A value of 3 usually means all lines belonging to that vertex have at least + // one side referencing the current sector. There are (rare) cases where this + // is not true, so a second check is done for all "opposite" vertices of the + // vertices with values 1 and 2: if the opposite vertex has a value of 3, but + // the line between the two vertices has no side referencing the current sector, + // then this falsely good vertex is added to the holes, too + + // Go for all the sectors + foreach (Sector s in General.Map.Map.Sectors) + { + List foundholes = new List(); + Dictionary vertices = new Dictionary(); + + // look at each side that belongs to the current sector + foreach (Sidedef sd in s.Sidedefs) + { + // Add the lines starting vertex to the Dictionary if it's not yet present + if (!vertices.ContainsKey(sd.Line.Start)) + { + vertices.Add(sd.Line.Start, 0); + } + + // Add the lines ending vertex to the Dictionary if it's not yet present + if (!vertices.ContainsKey(sd.Line.End)) + { + vertices.Add(sd.Line.End, 0); + } + + // enable the 1 bit of the start vertex and the 2 bit of the end vertex of the side + // This is from the sides point of view, where the side is always the "right" of line, + // so start and end are flipped depending if the current side is the front of the + // line or not + if (sd.IsFront == true) + { + vertices[sd.Line.Start] |= 1; + vertices[sd.Line.End] |= 2; + } + else + { + vertices[sd.Line.End] |= 1; + vertices[sd.Line.Start] |= 2; + } + } + + // look at all the vertices + foreach (KeyValuePair kvp in vertices) + { + // only look at bad vertices + if (kvp.Value != 3) + { + // add the current vertex to the holes list + if (!foundholes.Contains(kvp.Key)) + { + foundholes.Add(kvp.Key); + } + + // this checks if any vertices slipped past the first test + // look at all lines that share the current vertex + foreach (Linedef cl in kvp.Key.Linedefs) + { + // the line does not has its front or back side referencing the current sector + if (!((cl.Front != null && cl.Front.Sector == s) || (cl.Back != null && cl.Back.Sector == s))) + { + // if the opposite vertex of our current vertex is marked good something must be wrong, + // since the above check tells us that the line bewtween the vertices has no side referencing + // the current sector. So add the falsely marked vertex to the holes list + if (vertices.ContainsKey(cl.Start) && vertices[cl.Start] == 3 && !foundholes.Contains(cl.Start)) + { + foundholes.Add(cl.Start); + } + + if (vertices.ContainsKey(cl.End) && vertices[cl.End] == 3 && !foundholes.Contains(cl.End)) + { + foundholes.Add(cl.End); + } + } + } + } + } + + // Add report when holes have been found + if (foundholes.Count > 0) + SubmitResult(new ResultSectorUnclosed(s, foundholes)); + + // Handle thread interruption + try { Thread.Sleep(0); } + catch (ThreadInterruptedException) { return; } + + // We are making progress! + if ((++progress / PROGRESS_STEP) > stepprogress) + { + stepprogress = (progress / PROGRESS_STEP); + AddProgress(1); + } + } + } + + #endregion + } +} diff --git a/Source/Plugins/BuilderModes/ErrorChecks/CheckLineReferences.cs b/Source/Plugins/BuilderModes/ErrorChecks/CheckLineReferences.cs new file mode 100644 index 0000000..3df5599 --- /dev/null +++ b/Source/Plugins/BuilderModes/ErrorChecks/CheckLineReferences.cs @@ -0,0 +1,109 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using CodeImp.DoomBuilder.Windows; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Geometry; +using CodeImp.DoomBuilder.Editing; +using CodeImp.DoomBuilder.Actions; +using CodeImp.DoomBuilder.Types; +using CodeImp.DoomBuilder.Config; +using System.Threading; + +#endregion + +namespace CodeImp.DoomBuilder.BuilderModes +{ + [ErrorChecker("Check line references", true, 50)] + public class CheckLineReferences : ErrorChecker + { + #region ================== Constants + + private int PROGRESS_STEP = 1000; + + #endregion + + #region ================== Constructor / Destructor + + // Constructor + public CheckLineReferences() + { + // Total progress is done when all lines are checked + SetTotalProgress(General.Map.Map.Linedefs.Count / PROGRESS_STEP); + } + + #endregion + + #region ================== Methods + + // This runs the check + public override void Run() + { + int progress = 0; + int stepprogress = 0; + + // Go for all the liendefs + foreach (Linedef l in General.Map.Map.Linedefs) + { + // Line has a back side but no front side? + if ((l.Back != null) && (l.Front == null)) + { + SubmitResult(new ResultLineMissingFront(l)); + } + // Line has no sides at all? + else if ((l.Back == null) && (l.Front == null)) + { + SubmitResult(new ResultLineMissingSides(l)); + } + // Line is marked double-sided, but has only one side? + else if (l.IsFlagSet(General.Map.Config.DoubleSidedFlag) && (l.Back == null)) + { + SubmitResult(new ResultLineNotDoubleSided(l)); + } + // Line is marked single-sided, and has two sides? + else if (!l.IsFlagSet(General.Map.Config.DoubleSidedFlag) && (l.Back != null)) + { + SubmitResult(new ResultLineNotSingleSided(l)); + } + + // Handle thread interruption + try { Thread.Sleep(0); } + catch (ThreadInterruptedException) { return; } + + // We are making progress! + if ((++progress / PROGRESS_STEP) > stepprogress) + { + stepprogress = (progress / PROGRESS_STEP); + AddProgress(1); + } + } + } + + #endregion + } +} diff --git a/Source/Plugins/BuilderModes/ErrorChecks/CheckMissingTextures.cs b/Source/Plugins/BuilderModes/ErrorChecks/CheckMissingTextures.cs new file mode 100644 index 0000000..f734e13 --- /dev/null +++ b/Source/Plugins/BuilderModes/ErrorChecks/CheckMissingTextures.cs @@ -0,0 +1,117 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using CodeImp.DoomBuilder.Windows; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Geometry; +using CodeImp.DoomBuilder.Editing; +using CodeImp.DoomBuilder.Actions; +using CodeImp.DoomBuilder.Types; +using CodeImp.DoomBuilder.Config; +using System.Threading; +using CodeImp.DoomBuilder.Data; + +#endregion + +namespace CodeImp.DoomBuilder.BuilderModes +{ + [ErrorChecker("Check missing textures", true, 80)] + public class CheckMissingTextures : ErrorChecker + { + #region ================== Constants + + private int PROGRESS_STEP = 1000; + + #endregion + + #region ================== Constructor / Destructor + + // Constructor + public CheckMissingTextures() + { + // Total progress is done when all lines are checked + SetTotalProgress(General.Map.Map.Sidedefs.Count / PROGRESS_STEP); + } + + #endregion + + #region ================== Methods + + // This runs the check + public override void Run() + { + int progress = 0; + int stepprogress = 0; + + // Go for all the sidedefs + foreach (Sidedef sd in General.Map.Map.Sidedefs) + { + // Check upper texture. Also make sure not to return a false + // positive if the sector on the other side has the ceiling + // set to be sky + if (sd.HighRequired() && sd.HighTexture[0] == '-') + { + if (sd.Other != null && sd.Other.Sector.CeilTexture != General.Map.Config.SkyFlatName) + { + SubmitResult(new ResultMissingTexture(sd, SidedefPart.Upper)); + } + } + + // Check middle texture + if (sd.MiddleRequired() && sd.MiddleTexture[0] == '-') + { + SubmitResult(new ResultMissingTexture(sd, SidedefPart.Middle)); + } + + // Check lower texture. Also make sure not to return a false + // positive if the sector on the other side has the floor + // set to be sky + if (sd.LowRequired() && sd.LowTexture[0] == '-') + { + if (sd.Other != null && sd.Other.Sector.FloorTexture != General.Map.Config.SkyFlatName) + { + SubmitResult(new ResultMissingTexture(sd, SidedefPart.Lower)); + } + } + + // Handle thread interruption + try { Thread.Sleep(0); } + catch (ThreadInterruptedException) { return; } + + // We are making progress! + if ((++progress / PROGRESS_STEP) > stepprogress) + { + stepprogress = (progress / PROGRESS_STEP); + AddProgress(1); + } + } + } + + #endregion + } +} diff --git a/Source/Plugins/BuilderModes/ErrorChecks/CheckOverlappingLines.cs b/Source/Plugins/BuilderModes/ErrorChecks/CheckOverlappingLines.cs new file mode 100644 index 0000000..fa38731 --- /dev/null +++ b/Source/Plugins/BuilderModes/ErrorChecks/CheckOverlappingLines.cs @@ -0,0 +1,137 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using CodeImp.DoomBuilder.Windows; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Geometry; +using CodeImp.DoomBuilder.Editing; +using CodeImp.DoomBuilder.Actions; +using CodeImp.DoomBuilder.Types; +using CodeImp.DoomBuilder.Config; +using System.Threading; + +#endregion + +namespace CodeImp.DoomBuilder.BuilderModes +{ + [ErrorChecker("Check overlapping lines", true, 500)] + public class CheckOverlappingLines : ErrorChecker + { + #region ================== Constants + + private const int PROGRESS_STEP = 100; + + #endregion + + #region ================== Constructor / Destructor + + // Constructor + public CheckOverlappingLines() + { + // Total progress is done when all lines are checked + SetTotalProgress(General.Map.Map.Linedefs.Count / PROGRESS_STEP); + } + + #endregion + + #region ================== Methods + + // This runs the check + public override void Run() + { + Dictionary donelines = new Dictionary(); + BlockMap blockmap = BuilderPlug.Me.ErrorCheckForm.BlockMap; + int progress = 0; + int stepprogress = 0; + + // Go for all the liendefs + foreach (Linedef l in General.Map.Map.Linedefs) + { + // Check if not already done + if (!donelines.ContainsKey(l)) + { + // And go for all the linedefs that could overlap + List blocks = blockmap.GetLineBlocks(l.Start.Position, l.End.Position); + Dictionary doneblocklines = new Dictionary(blocks.Count * 3); + foreach (BlockEntry b in blocks) + { + foreach (Linedef d in b.Lines) + { + // Not the same line and not already checked + if (!object.ReferenceEquals(l, d) && !doneblocklines.ContainsKey(d)) + { + float lu, du; + + // Check if the lines touch. Note that I don't include 0.0 and 1.0 here because + // the lines may be touching at the ends when sharing the same vertex. + if (l.Line.GetIntersection(d.Line, out du, out lu)) + { + if ((lu > 0.0f) && (lu < 1.0f) && (du > 0.0f) && (du < 1.0f)) + { + // Check if not the same sector on all sides + Sector samesector = null; + if (l.Front != null) samesector = l.Front.Sector; + else if (l.Back != null) samesector = l.Back.Sector; + else if (d.Front != null) samesector = d.Front.Sector; + else if (d.Back != null) samesector = d.Back.Sector; + if ((l.Front == null) || (l.Front.Sector != samesector)) samesector = null; + else if ((l.Back == null) || (l.Back.Sector != samesector)) samesector = null; + else if ((d.Front == null) || (d.Front.Sector != samesector)) samesector = null; + else if ((d.Back == null) || (d.Back.Sector != samesector)) samesector = null; + + if (samesector == null) + { + SubmitResult(new ResultLineOverlapping(l, d)); + donelines[d] = d; + } + } + } + + // Checked + doneblocklines.Add(d, d); + } + } + } + } + + // Handle thread interruption + try { Thread.Sleep(0); } + catch (ThreadInterruptedException) { return; } + + // We are making progress! + if ((++progress / PROGRESS_STEP) > stepprogress) + { + stepprogress = (progress / PROGRESS_STEP); + AddProgress(1); + } + } + } + + #endregion + } +} diff --git a/Source/Plugins/BuilderModes/ErrorChecks/CheckStuckThings.cs b/Source/Plugins/BuilderModes/ErrorChecks/CheckStuckThings.cs new file mode 100644 index 0000000..7e4e676 --- /dev/null +++ b/Source/Plugins/BuilderModes/ErrorChecks/CheckStuckThings.cs @@ -0,0 +1,276 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections.Generic; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Geometry; +using CodeImp.DoomBuilder.Config; +using System.Threading; +using System.Drawing; + +#endregion + +namespace CodeImp.DoomBuilder.BuilderModes +{ + [ErrorChecker("Check stuck things", true, 1000)] + public class CheckStuckThings : ErrorChecker + { + #region ================== Constants + + private const int PROGRESS_STEP = 10; + + #endregion + + #region ================== Constructor / Destructor + + // Constructor + public CheckStuckThings() + { + // Total progress is done when all things are checked + SetTotalProgress(General.Map.Map.Things.Count / PROGRESS_STEP); + } + + #endregion + + #region ================== Methods + + // This runs the check + public override void Run() + { + BlockMap blockmap = BuilderPlug.Me.ErrorCheckForm.BlockMap; + int progress = 0; + int stepprogress = 0; + float maxradius = 0; + Dictionary> processedthingpairs = new Dictionary>(); //mxd + + foreach (ThingTypeInfo tti in General.Map.Data.ThingTypes) + { + if (tti.Radius > maxradius) maxradius = tti.Radius; + } + + // Go for all the things + foreach (Thing t in General.Map.Map.Things) + { + ThingTypeInfo info = General.Map.Data.GetThingInfo(t.Type); + bool stuck = false; + + // Check this thing for getting stuck? + if ((info.ErrorCheck == ThingTypeInfo.THING_ERROR_INSIDE_STUCK) && + (info.Blocking > ThingTypeInfo.THING_BLOCKING_NONE)) + { + // Make square coordinates from thing + float blockingsize = t.Size; + Vector2D lt = new Vector2D(t.Position.x - blockingsize, t.Position.y - blockingsize); + Vector2D rb = new Vector2D(t.Position.x + blockingsize, t.Position.y + blockingsize); + Vector2D bmlt = new Vector2D(t.Position.x - maxradius, t.Position.y - maxradius); + Vector2D bmrb = new Vector2D(t.Position.x + maxradius, t.Position.y + maxradius); + + // Go for all the lines to see if this thing is stuck + List blocks = blockmap.GetSquareRange(new RectangleF((float)bmlt.x, (float)bmlt.y, (float)(bmrb.x - bmlt.x), (float)(bmrb.y - bmlt.y))); + Dictionary doneblocklines = new Dictionary(blocks.Count * 3); + + foreach (BlockEntry b in blocks) + { + foreach (Linedef l in b.Lines) + { + // Only test when sinlge-sided, two-sided + impassable and not already checked + if (((l.Back == null) || l.IsFlagSet(General.Map.Config.ImpassableFlag)) && !doneblocklines.ContainsKey(l)) + { + // Test if line ends are inside the thing + if (PointInRect(lt, rb, l.Start.Position) || PointInRect(lt, rb, l.End.Position)) + { + // Thing stuck in line! + stuck = true; + SubmitResult(new ResultStuckThingInLine(t, l)); + } + // Test if the line intersects the square + else if (Line2D.GetIntersection(l.Start.Position, l.End.Position, lt.x, lt.y, rb.x, lt.y) || + Line2D.GetIntersection(l.Start.Position, l.End.Position, rb.x, lt.y, rb.x, rb.y) || + Line2D.GetIntersection(l.Start.Position, l.End.Position, rb.x, rb.y, lt.x, rb.y) || + Line2D.GetIntersection(l.Start.Position, l.End.Position, lt.x, rb.y, lt.x, lt.y)) + { + // Thing stuck in line! + stuck = true; + SubmitResult(new ResultStuckThingInLine(t, l)); + } + + // Checked + doneblocklines.Add(l, l); + } + } + + // Check if thing is stuck in other things + if (info.Blocking != ThingTypeInfo.THING_BLOCKING_NONE) + { + foreach (Thing ot in b.Things) + { + // Don't compare the thing with itself + if (t.Index == ot.Index) continue; + + // mxd. Don't compare already processed stuff + if (processedthingpairs.ContainsKey(t.Index) && processedthingpairs[t.Index].Contains(ot.Index)) continue; + + // Only check of items that can block + if (General.Map.Data.GetThingInfo(ot.Type).Blocking == ThingTypeInfo.THING_BLOCKING_NONE) continue; + + // need to compare the flags + if (FlagsOverlap(t, ot) && ThingsOverlap(t, ot)) + { + stuck = true; + SubmitResult(new ResultStuckThingInThing(t, ot)); + } + + //mxd. Prepare collections + if (!processedthingpairs.ContainsKey(t.Index)) processedthingpairs.Add(t.Index, new HashSet()); + if (!processedthingpairs.ContainsKey(ot.Index)) processedthingpairs.Add(ot.Index, new HashSet()); + + //mxd. Add both ways + processedthingpairs[t.Index].Add(ot.Index); + processedthingpairs[ot.Index].Add(t.Index); + } + } + } + } + + // Check this thing for being outside the map? + if (!stuck && info.ErrorCheck >= ThingTypeInfo.THING_ERROR_INSIDE) + { + // Get the nearest line to see if the thing is outside the map + bool outside; + Linedef l = General.Map.Map.NearestLinedef(t.Position); + if (l.SideOfLine(t.Position) <= 0) + { + outside = (l.Front == null); + } + else + { + outside = (l.Back == null); + } + + // Outside the map? + if (outside) + { + // Make result + SubmitResult(new ResultThingOutside(t)); + } + } + + // Handle thread interruption + try { Thread.Sleep(0); } + catch (ThreadInterruptedException) { return; } + + // We are making progress! + if ((++progress / PROGRESS_STEP) > stepprogress) + { + stepprogress = (progress / PROGRESS_STEP); + AddProgress(1); + } + } + } + + // Point in rect? + private static bool PointInRect(Vector2D lt, Vector2D rb, Vector2D p) + { + return (p.x >= lt.x) && (p.x <= rb.x) && (p.y <= lt.y) && (p.y >= rb.y); + } + + // Checks if two things overlap + private static bool ThingsOverlap(Thing t1, Thing t2) + { + Vector3D p1 = t1.Position; + Vector3D p2 = t2.Position; + ThingTypeInfo t1info = General.Map.Data.GetThingInfo(t1.Type); + ThingTypeInfo t2info = General.Map.Data.GetThingInfo(t2.Type); + + // simple bounding box collision detection + if (p1.x + t1.Size <= p2.x - t2.Size || + p1.x - t1.Size >= p2.x + t2.Size || + p1.y - t1.Size >= p2.y + t2.Size || + p1.y + t1.Size <= p2.y - t2.Size) + { + return false; + } + + // if either thing blocks full height there's no need to check the z-axis + if (t1info.Blocking == ThingTypeInfo.THING_BLOCKING_FULL || t2info.Blocking == ThingTypeInfo.THING_BLOCKING_FULL) + return true; + + // check z-axis + if (p1.z > p2.z + t2info.Height || p1.z + t1info.Height < p2.z) + return false; + + return true; + } + + // Checks if the flags of two things overlap (i.e. if they show up at the same time) + private static bool FlagsOverlap(Thing t1, Thing t2) + { + if (General.Map.Config.ThingFlagsCompare.Count < 1) return true; //mxd. Otherwise, no things will ever overlap when ThingFlagsCompare is empty + Dictionary results = new Dictionary(General.Map.Config.ThingFlagsCompare.Count); + + // Go through all flags in all groups and check if they overlap + foreach (ThingFlagsCompareGroup group in General.Map.Config.ThingFlagsCompare.Values) + results[group.Name] = group.Compare(t1, t2); + + // Process Required/IgnoredGroups + foreach (ThingFlagsCompareResult result in results.Values) + { + // Group matters only when it contains overlapping flags + if (result.Result == 1) + { + // Ignore this group when RequiredGroup flags don't overlap + foreach (string requiredgroup in result.RequiredGroups) + { + if (results[requiredgroup].Result != 1) + { + result.Result = 0; + break; + } + } + + // Ignore other groups when this one's flags overlap + if (result.Result == 1) + { + foreach (string ignoredgroup in result.IgnoredGroups) + results[ignoredgroup].Result = 0; + } + } + } + + // Count overlapping groups + int overlappinggroupscount = 0; + int totalgroupscount = results.Values.Count; + foreach (ThingFlagsCompareResult result in results.Values) + { + switch (result.Result) + { + case 1: overlappinggroupscount++; break; // Group flags overlap + case 0: totalgroupscount--; break; // Ignored group should be ignored + case -1: return false; // Group flags don't overlap + default: throw new NotImplementedException("Unknown thing flags comparison result"); + } + } + + // All groups have to overlap for the things to show up at the same time + return (totalgroupscount > 0 && overlappinggroupscount == totalgroupscount); + } + + #endregion + } +} diff --git a/Source/Plugins/BuilderModes/ErrorChecks/CheckUnknownFlats.cs b/Source/Plugins/BuilderModes/ErrorChecks/CheckUnknownFlats.cs new file mode 100644 index 0000000..636efd8 --- /dev/null +++ b/Source/Plugins/BuilderModes/ErrorChecks/CheckUnknownFlats.cs @@ -0,0 +1,97 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using CodeImp.DoomBuilder.Windows; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Geometry; +using CodeImp.DoomBuilder.Editing; +using CodeImp.DoomBuilder.Actions; +using CodeImp.DoomBuilder.Types; +using CodeImp.DoomBuilder.Config; +using System.Threading; +using CodeImp.DoomBuilder.Data; + +#endregion + +namespace CodeImp.DoomBuilder.BuilderModes +{ + [ErrorChecker("Check unknown flats", true, 40)] + public class CheckUnknownFlats : ErrorChecker + { + #region ================== Constants + + private int PROGRESS_STEP = 1000; + + #endregion + + #region ================== Constructor / Destructor + + // Constructor + public CheckUnknownFlats() + { + // Total progress is done when all sectors are checked + SetTotalProgress(General.Map.Map.Sectors.Count / PROGRESS_STEP); + } + + #endregion + + #region ================== Methods + + // This runs the check + public override void Run() + { + int progress = 0; + int stepprogress = 0; + + // Go for all the sectors + foreach (Sector s in General.Map.Map.Sectors) + { + // Check floor texture + if (!General.Map.Data.GetFlatExists(s.FloorTexture)) + SubmitResult(new ResultUnknownFlat(s, false)); + + // Check ceiling texture + if (!General.Map.Data.GetFlatExists(s.CeilTexture)) + SubmitResult(new ResultUnknownFlat(s, true)); + + // Handle thread interruption + try { Thread.Sleep(0); } + catch (ThreadInterruptedException) { return; } + + // We are making progress! + if ((++progress / PROGRESS_STEP) > stepprogress) + { + stepprogress = (progress / PROGRESS_STEP); + AddProgress(1); + } + } + } + + #endregion + } +} diff --git a/Source/Plugins/BuilderModes/ErrorChecks/CheckUnknownTextures.cs b/Source/Plugins/BuilderModes/ErrorChecks/CheckUnknownTextures.cs new file mode 100644 index 0000000..d66bde2 --- /dev/null +++ b/Source/Plugins/BuilderModes/ErrorChecks/CheckUnknownTextures.cs @@ -0,0 +1,110 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using CodeImp.DoomBuilder.Windows; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Geometry; +using CodeImp.DoomBuilder.Editing; +using CodeImp.DoomBuilder.Actions; +using CodeImp.DoomBuilder.Types; +using CodeImp.DoomBuilder.Config; +using System.Threading; +using CodeImp.DoomBuilder.Data; + +#endregion + +namespace CodeImp.DoomBuilder.BuilderModes +{ + [ErrorChecker("Check unknown textures", true, 60)] + public class CheckUnknownTextures : ErrorChecker + { + #region ================== Constants + + private int PROGRESS_STEP = 1000; + + #endregion + + #region ================== Constructor / Destructor + + // Constructor + public CheckUnknownTextures() + { + // Total progress is done when all lines are checked + SetTotalProgress(General.Map.Map.Sidedefs.Count / PROGRESS_STEP); + } + + #endregion + + #region ================== Methods + + // This runs the check + public override void Run() + { + int progress = 0; + int stepprogress = 0; + + // Go for all the sidedefs + foreach (Sidedef sd in General.Map.Map.Sidedefs) + { + // Check upper texture + if (sd.HighRequired() && ((sd.HighTexture.Length < 1) || (sd.HighTexture[0] != '-'))) + { + if (!General.Map.Data.GetTextureExists(sd.LongHighTexture)) + SubmitResult(new ResultUnknownTexture(sd, SidedefPart.Upper)); + } + + // Check middle texture + if (sd.MiddleRequired() && ((sd.MiddleTexture.Length < 1) || (sd.MiddleTexture[0] != '-'))) + { + if (!General.Map.Data.GetTextureExists(sd.LongMiddleTexture)) + SubmitResult(new ResultUnknownTexture(sd, SidedefPart.Middle)); + } + + // Check lower texture + if (sd.LowRequired() && ((sd.LowTexture.Length < 1) || (sd.LowTexture[0] != '-'))) + { + if (!General.Map.Data.GetTextureExists(sd.LongLowTexture)) + SubmitResult(new ResultUnknownTexture(sd, SidedefPart.Lower)); + } + + // Handle thread interruption + try { Thread.Sleep(0); } + catch (ThreadInterruptedException) { return; } + + // We are making progress! + if ((++progress / PROGRESS_STEP) > stepprogress) + { + stepprogress = (progress / PROGRESS_STEP); + AddProgress(1); + } + } + } + + #endregion + } +} diff --git a/Source/Plugins/BuilderModes/ErrorChecks/ErrorChecker.cs b/Source/Plugins/BuilderModes/ErrorChecks/ErrorChecker.cs new file mode 100644 index 0000000..eacee50 --- /dev/null +++ b/Source/Plugins/BuilderModes/ErrorChecks/ErrorChecker.cs @@ -0,0 +1,113 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using CodeImp.DoomBuilder.Windows; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Geometry; +using CodeImp.DoomBuilder.Editing; +using CodeImp.DoomBuilder.Actions; +using CodeImp.DoomBuilder.Types; +using CodeImp.DoomBuilder.Config; + +#endregion + +namespace CodeImp.DoomBuilder.BuilderModes +{ + // NOTE: An ErrorChecker may NEVER modify the map, because it runs multithreaded. + // Do not even change element properties such as 'marked' and 'selected'! + public class ErrorChecker : IComparable + { + #region ================== Variables + + private int lastprogress; + private int totalprogress = -1; + protected ErrorCheckerAttribute attribs; + + #endregion + + #region ================== Properties + + public int TotalProgress { get { return totalprogress; } } + + #endregion + + #region ================== Constructor / Destructor + + // Constructor + // Override this to determine and set the total progress + public ErrorChecker() + { + // Initialize + object[] attrs = this.GetType().GetCustomAttributes(typeof(ErrorCheckerAttribute), true); + attribs = (ErrorCheckerAttribute)attrs[0]; + } + + #endregion + + #region ================== Methods + + // Override this to run your check + // Use a Sleep and Try/Catch to handle thread interruption + public virtual void Run() + { + if (totalprogress < 0) throw new InvalidOperationException("You must set the total progress through the SetTotalProgress method before this check can be performed!"); + lastprogress = 0; + } + + // This submits a result to show in the results list + protected void SubmitResult(ErrorResult result) + { + BuilderPlug.Me.ErrorCheckForm.SubmitResult(result); + } + + // This reports a change in progress + protected void AddProgress(int amount) + { + // Make changes + if (amount < 0) amount = 0; + if ((lastprogress + amount) > totalprogress) throw new InvalidOperationException("Cannot exceed total progress amount!"); + lastprogress += amount; + BuilderPlug.Me.ErrorCheckForm.AddProgressValue(amount); + } + + // This sets the total progress + protected void SetTotalProgress(int totalprogress) + { + if (totalprogress < 0) throw new ArgumentOutOfRangeException("Total progress cannot be less than zero!"); + this.totalprogress = totalprogress; + } + + // This is for sorting by cost + public int CompareTo(ErrorChecker other) + { + return (other.attribs.Cost - this.attribs.Cost); + } + + #endregion + } +} diff --git a/Source/Plugins/BuilderModes/ErrorChecks/ErrorCheckerAttribute.cs b/Source/Plugins/BuilderModes/ErrorChecks/ErrorCheckerAttribute.cs new file mode 100644 index 0000000..5186302 --- /dev/null +++ b/Source/Plugins/BuilderModes/ErrorChecks/ErrorCheckerAttribute.cs @@ -0,0 +1,83 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using CodeImp.DoomBuilder.Windows; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Geometry; +using CodeImp.DoomBuilder.Editing; +using CodeImp.DoomBuilder.Actions; +using CodeImp.DoomBuilder.Types; +using CodeImp.DoomBuilder.Config; + +#endregion + +namespace CodeImp.DoomBuilder.BuilderModes +{ + [AttributeUsage(AttributeTargets.Class, Inherited = true, AllowMultiple = false)] + public class ErrorCheckerAttribute : Attribute + { + #region ================== Variables + + private string displayname; + private bool defaultchecked; + private int cost; + + #endregion + + #region ================== Properties + + public string DisplayName { get { return displayname; } set { displayname = value; } } + public bool DefaultChecked { get { return defaultchecked; } set { defaultchecked = value; } } + public int Cost { get { return cost; } set { cost = value; } } + + #endregion + + #region ================== Constructor / Destructor + + // Constructor + public ErrorCheckerAttribute(string displayname, bool defaultchecked, int cost) + { + // Initialize + this.displayname = displayname; + this.defaultchecked = defaultchecked; + this.cost = cost; + } + + #endregion + + #region ================== Methods + + // String representation + public override string ToString() + { + return displayname; + } + + #endregion + } +} diff --git a/Source/Plugins/BuilderModes/ErrorChecks/ErrorResult.cs b/Source/Plugins/BuilderModes/ErrorChecks/ErrorResult.cs new file mode 100644 index 0000000..75ff84c --- /dev/null +++ b/Source/Plugins/BuilderModes/ErrorChecks/ErrorResult.cs @@ -0,0 +1,193 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using CodeImp.DoomBuilder.Windows; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Geometry; +using CodeImp.DoomBuilder.Editing; +using CodeImp.DoomBuilder.Actions; +using CodeImp.DoomBuilder.Types; +using CodeImp.DoomBuilder.Config; +using System.Drawing; + +#endregion + +namespace CodeImp.DoomBuilder.BuilderModes +{ + public class ErrorResult + { + #region ================== Variables + + protected string description; + protected List viewobjects; + + #endregion + + #region ================== Properties + + public string Description { get { return description; } } + + // Override these properties to create buttons + public virtual int Buttons { get { return 0; } } + public virtual string Button1Text { get { return ""; } } + public virtual string Button2Text { get { return ""; } } + public virtual string Button3Text { get { return ""; } } + + #endregion + + #region ================== Constructor / Destructor + + // Constructor + public ErrorResult() + { + // Initialize + viewobjects = new List(1); + } + + #endregion + + #region ================== Methods + + // When the first button is clicked + // Return true when map geometry or things have been added/removed so that the checker can restart + public virtual bool Button1Click() + { + return false; + } + + // When the second button is clicked + // Return true when map geometry or things have been added/removed so that the checker can restart + public virtual bool Button2Click() + { + return false; + } + + // When the third button is clicked + // Return true when map geometry or things have been added/removed so that the checker can restart + public virtual bool Button3Click() + { + return false; + } + + // This must return the string that is displayed in the listbox + public override string ToString() + { + return "Unknown result"; + } + + // This is called for rendering + public virtual void PlotSelection(IRenderer2D renderer) + { + } + + // This is called for rendering + public virtual void RenderThingsSelection(IRenderer2D renderer) + { + } + + // This is called for rendering + public virtual void RenderOverlaySelection(IRenderer2D renderer) + { + } + + // Call this to zoom in on the given selection + public void ZoomToObject() + { + List points = new List(); + RectangleF area = MapSet.CreateEmptyArea(); + + // Add all points to a list + foreach (MapElement obj in viewobjects) + { + if (obj is Vertex) + { + points.Add((obj as Vertex).Position); + } + else if (obj is Linedef) + { + points.Add((obj as Linedef).Start.Position); + points.Add((obj as Linedef).End.Position); + } + else if (obj is Sidedef) + { + points.Add((obj as Sidedef).Line.Start.Position); + points.Add((obj as Sidedef).Line.End.Position); + } + else if (obj is Sector) + { + Sector s = (obj as Sector); + foreach (Sidedef sd in s.Sidedefs) + { + points.Add(sd.Line.Start.Position); + points.Add(sd.Line.End.Position); + } + } + else if (obj is Thing) + { + Thing t = (obj as Thing); + Vector2D p = (Vector2D)t.Position; + points.Add(p); + points.Add(p + new Vector2D(t.Size * 2.0f, t.Size * 2.0f)); + points.Add(p + new Vector2D(t.Size * 2.0f, -t.Size * 2.0f)); + points.Add(p + new Vector2D(-t.Size * 2.0f, t.Size * 2.0f)); + points.Add(p + new Vector2D(-t.Size * 2.0f, -t.Size * 2.0f)); + } + else + { + General.Fail("Unknown object given to zoom in on."); + } + } + + // Make a view area from the points + foreach (Vector2D p in points) area = MapSet.IncreaseArea(area, p); + + // Make the area square, using the largest side + if (area.Width > area.Height) + { + float delta = area.Width - area.Height; + area.Y -= delta * 0.5f; + area.Height += delta; + } + else + { + float delta = area.Height - area.Width; + area.X -= delta * 0.5f; + area.Width += delta; + } + + // Add padding + area.Inflate(100f, 100f); + + // Zoom to area + ClassicMode editmode = (General.Editing.Mode as ClassicMode); + editmode.CenterOnArea(area, 0.6f); + } + + #endregion + } +} diff --git a/Source/Plugins/BuilderModes/ErrorChecks/ResultLineMissingFront.cs b/Source/Plugins/BuilderModes/ErrorChecks/ResultLineMissingFront.cs new file mode 100644 index 0000000..22fec6f --- /dev/null +++ b/Source/Plugins/BuilderModes/ErrorChecks/ResultLineMissingFront.cs @@ -0,0 +1,146 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using CodeImp.DoomBuilder.Windows; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Geometry; +using CodeImp.DoomBuilder.Editing; +using CodeImp.DoomBuilder.Actions; +using CodeImp.DoomBuilder.Types; +using CodeImp.DoomBuilder.Config; + +#endregion + +namespace CodeImp.DoomBuilder.BuilderModes +{ + public class ResultLineMissingFront : ErrorResult + { + #region ================== Variables + + private Linedef line; + private int buttons; + private Sidedef copysidedef; + + #endregion + + #region ================== Properties + + public override int Buttons { get { return buttons; } } + public override string Button1Text { get { return "Flip Linedef"; } } + public override string Button2Text { get { return "Create Sidedef"; } } + + #endregion + + #region ================== Constructor / Destructor + + // Constructor + public ResultLineMissingFront(Linedef l) + { + // Initialize + this.line = l; + this.viewobjects.Add(l); + this.description = "This linedef has a back sidedef, but is missing a front sidedef. " + + "A line must have at least a front side and optionally a back side! " + + "Click Flip Linedef if the line is supposed to be single-sided."; + + // One solution is to flip the sidedefs + buttons = 1; + + // Check if the linedef can join a sector on the side where it is missing a sidedef + bool fixable = false; + List sides = Tools.FindPotentialSectorAt(l, true); + if (sides != null) + { + foreach (LinedefSide sd in sides) + { + // If any of the sides lies along a sidedef, then we can copy + // that sidedef to fix the missing sidedef on this line. + if (sd.Front && (sd.Line.Front != null)) + { + copysidedef = sd.Line.Front; + fixable = true; + break; + } + else if (!sd.Front && (sd.Line.Back != null)) + { + copysidedef = sd.Line.Back; + fixable = true; + break; + } + } + } + + // Fixable? + if (fixable) + { + buttons++; + this.description += " Or click Create Sidedef to rebuild the missing sidedef (making the line double-sided)."; + } + } + + #endregion + + #region ================== Methods + + // This must return the string that is displayed in the listbox + public override string ToString() + { + return "Linedef is missing front side"; + } + + // Rendering + public override void PlotSelection(IRenderer2D renderer) + { + renderer.PlotLinedef(line, General.Colors.Selection); + renderer.PlotVertex(line.Start, ColorCollection.VERTICES); + renderer.PlotVertex(line.End, ColorCollection.VERTICES); + } + + // Fix by flipping linedefs + public override bool Button1Click() + { + line.FlipSidedefs(); + General.Map.Map.Update(); + return true; + } + + // Fix by creating a sidedef + public override bool Button2Click() + { + General.Map.UndoRedo.CreateUndo("Create front sidedef"); + Sidedef newside = General.Map.Map.CreateSidedef(line, true, copysidedef.Sector); + if (newside == null) return false; + copysidedef.CopyPropertiesTo(newside); + line.ApplySidedFlags(); + General.Map.Map.Update(); + return true; + } + + #endregion + } +} diff --git a/Source/Plugins/BuilderModes/ErrorChecks/ResultLineMissingSides.cs b/Source/Plugins/BuilderModes/ErrorChecks/ResultLineMissingSides.cs new file mode 100644 index 0000000..22fc420 --- /dev/null +++ b/Source/Plugins/BuilderModes/ErrorChecks/ResultLineMissingSides.cs @@ -0,0 +1,209 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using CodeImp.DoomBuilder.Windows; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Geometry; +using CodeImp.DoomBuilder.Editing; +using CodeImp.DoomBuilder.Actions; +using CodeImp.DoomBuilder.Types; +using CodeImp.DoomBuilder.Config; + +#endregion + +namespace CodeImp.DoomBuilder.BuilderModes +{ + public class ResultLineMissingSides : ErrorResult + { + #region ================== Variables + + private Linedef line; + private int buttons; + private Sidedef copysidedeffront; + private Sidedef copysidedefback; + + #endregion + + #region ================== Properties + + public override int Buttons { get { return buttons; } } + public override string Button1Text { get { return "Create One Side"; } } + public override string Button2Text { get { return "Create Both Sides"; } } + + #endregion + + #region ================== Constructor / Destructor + + // Constructor + public ResultLineMissingSides(Linedef l) + { + List sides; + bool fixable; + + // Initialize + this.line = l; + this.viewobjects.Add(l); + this.description = "This linedef is missing front and back sidedefs." + + "A line must have at least a front side and optionally a back side!"; + + buttons = 0; + + // Check if we can join a sector on the front side + fixable = false; + sides = Tools.FindPotentialSectorAt(l, true); + if (sides != null) + { + foreach (LinedefSide sd in sides) + { + // If any of the sides lies along a sidedef, then we can copy + // that sidedef to fix the missing sidedef on this line. + if (sd.Front && (sd.Line.Front != null)) + { + copysidedeffront = sd.Line.Front; + fixable = true; + break; + } + else if (!sd.Front && (sd.Line.Back != null)) + { + copysidedeffront = sd.Line.Back; + fixable = true; + break; + } + } + } + + // Fixable? + if (fixable) buttons++; + + // Check if we can join a sector on the back side + fixable = false; + sides = Tools.FindPotentialSectorAt(l, false); + if (sides != null) + { + foreach (LinedefSide sd in sides) + { + // If any of the sides lies along a sidedef, then we can copy + // that sidedef to fix the missing sidedef on this line. + if (sd.Front && (sd.Line.Front != null)) + { + copysidedefback = sd.Line.Front; + fixable = true; + break; + } + else if (!sd.Front && (sd.Line.Back != null)) + { + copysidedefback = sd.Line.Back; + fixable = true; + break; + } + } + } + + // Fixable? + if (fixable) buttons++; + + // Now make a fine description + switch (buttons) + { + case 0: description += " Doom Builder could not find a solution to fix this line."; break; + case 1: description += " Click Create One Side to rebuild a single sidedef, making this line single-sided."; break; + case 2: description += " Click Create Both Side to rebuild both sides of the line, making this line double-sided."; break; + } + } + + #endregion + + #region ================== Methods + + // This must return the string that is displayed in the listbox + public override string ToString() + { + return "Linedef is missing both sides"; + } + + // Rendering + public override void PlotSelection(IRenderer2D renderer) + { + renderer.PlotLinedef(line, General.Colors.Selection); + renderer.PlotVertex(line.Start, ColorCollection.VERTICES); + renderer.PlotVertex(line.End, ColorCollection.VERTICES); + } + + // Fix a single side + public override bool Button1Click() + { + // On which side can we fix? + if (copysidedeffront != null) + { + // Front + General.Map.UndoRedo.CreateUndo("Create front sidedef"); + Sidedef newside = General.Map.Map.CreateSidedef(line, true, copysidedeffront.Sector); + if (newside == null) return false; + copysidedeffront.CopyPropertiesTo(newside); + } + else if (copysidedefback != null) + { + // Back + // Because the line is single-sided, we make the sidedef on the front. + // We will then flip it to make sure to ends up in the right position. + General.Map.UndoRedo.CreateUndo("Create front sidedef"); + Sidedef newside = General.Map.Map.CreateSidedef(line, true, copysidedefback.Sector); + if (newside == null) return false; + copysidedefback.CopyPropertiesTo(newside); + line.FlipVertices(); + } + + line.ApplySidedFlags(); + General.Map.Map.Update(); + return true; + } + + // Fix both sides + public override bool Button2Click() + { + Sidedef newside; + General.Map.UndoRedo.CreateUndo("Create sidedefs"); + + // Front + newside = General.Map.Map.CreateSidedef(line, true, copysidedeffront.Sector); + if (newside == null) return false; + copysidedeffront.CopyPropertiesTo(newside); + + // Back + newside = General.Map.Map.CreateSidedef(line, false, copysidedefback.Sector); + if (newside == null) return false; + copysidedefback.CopyPropertiesTo(newside); + + line.ApplySidedFlags(); + General.Map.Map.Update(); + return true; + } + + #endregion + } +} diff --git a/Source/Plugins/BuilderModes/ErrorChecks/ResultLineNotDoubleSided.cs b/Source/Plugins/BuilderModes/ErrorChecks/ResultLineNotDoubleSided.cs new file mode 100644 index 0000000..7e6b712 --- /dev/null +++ b/Source/Plugins/BuilderModes/ErrorChecks/ResultLineNotDoubleSided.cs @@ -0,0 +1,145 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using CodeImp.DoomBuilder.Windows; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Geometry; +using CodeImp.DoomBuilder.Editing; +using CodeImp.DoomBuilder.Actions; +using CodeImp.DoomBuilder.Types; +using CodeImp.DoomBuilder.Config; + +#endregion + +namespace CodeImp.DoomBuilder.BuilderModes +{ + public class ResultLineNotDoubleSided : ErrorResult + { + #region ================== Variables + + private Linedef line; + private int buttons; + private Sidedef copysidedef; + + #endregion + + #region ================== Properties + + public override int Buttons { get { return buttons; } } + public override string Button1Text { get { return "Make Single-Sided"; } } + public override string Button2Text { get { return "Create Sidedef"; } } + + #endregion + + #region ================== Constructor / Destructor + + // Constructor + public ResultLineNotDoubleSided(Linedef l) + { + // Initialize + this.line = l; + this.viewobjects.Add(l); + this.description = "This linedef is marked as double-sided, but is missing the back sidedef. Click Make Single-Sided to remove the double-sided flag from the line."; + + // One solution is to remove the double-sided flag + buttons = 1; + + // Check if the linedef can join a sector on the side where it is missing a sidedef + bool fixable = false; + List sides = Tools.FindPotentialSectorAt(l, false); + if (sides != null) + { + foreach (LinedefSide sd in sides) + { + // If any of the sides lies along a sidedef, then we can copy + // that sidedef to fix the missing sidedef on this line. + if (sd.Front && (sd.Line.Front != null)) + { + copysidedef = sd.Line.Front; + fixable = true; + break; + } + else if (!sd.Front && (sd.Line.Back != null)) + { + copysidedef = sd.Line.Back; + fixable = true; + break; + } + } + } + + // Fixable? + if (fixable) + { + buttons++; + this.description += " Or click Create Sidedef to rebuild the missing sidedef (making the line really double-sided)."; + } + } + + #endregion + + #region ================== Methods + + // This must return the string that is displayed in the listbox + public override string ToString() + { + return "Linedef is marked double-sided but has no back side"; + } + + // Rendering + public override void PlotSelection(IRenderer2D renderer) + { + renderer.PlotLinedef(line, General.Colors.Selection); + renderer.PlotVertex(line.Start, ColorCollection.VERTICES); + renderer.PlotVertex(line.End, ColorCollection.VERTICES); + } + + // Fix by flipping linedefs + public override bool Button1Click() + { + General.Map.UndoRedo.CreateUndo("Linedef flags change"); + line.ApplySidedFlags(); + General.Map.Map.Update(); + return true; + } + + // Fix by creating a sidedef + public override bool Button2Click() + { + General.Map.UndoRedo.CreateUndo("Create back sidedef"); + Sidedef newside = General.Map.Map.CreateSidedef(line, false, copysidedef.Sector); + if (newside == null) return false; + copysidedef.CopyPropertiesTo(newside); + line.ApplySidedFlags(); + General.Map.Map.Update(); + return true; + } + + #endregion + } +} diff --git a/Source/Plugins/BuilderModes/ErrorChecks/ResultLineNotSingleSided.cs b/Source/Plugins/BuilderModes/ErrorChecks/ResultLineNotSingleSided.cs new file mode 100644 index 0000000..57cdab3 --- /dev/null +++ b/Source/Plugins/BuilderModes/ErrorChecks/ResultLineNotSingleSided.cs @@ -0,0 +1,108 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using CodeImp.DoomBuilder.Windows; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Geometry; +using CodeImp.DoomBuilder.Editing; +using CodeImp.DoomBuilder.Actions; +using CodeImp.DoomBuilder.Types; +using CodeImp.DoomBuilder.Config; + +#endregion + +namespace CodeImp.DoomBuilder.BuilderModes +{ + public class ResultLineNotSingleSided : ErrorResult + { + #region ================== Variables + + private Linedef line; + + #endregion + + #region ================== Properties + + public override int Buttons { get { return 2; } } + public override string Button1Text { get { return "Make Double-Sided"; } } + public override string Button2Text { get { return "Remove Sidedef"; } } + + #endregion + + #region ================== Constructor / Destructor + + // Constructor + public ResultLineNotSingleSided(Linedef l) + { + // Initialize + this.line = l; + this.viewobjects.Add(l); + this.description = "This linedef is marked as single-sided, but has both a front and a back sidedef. Click Make Double-Sided to flag the line as double-sided." + + " Or click Remove Sidedef to remove the sidedef on the back side (making the line really single-sided)."; + } + + #endregion + + #region ================== Methods + + // This must return the string that is displayed in the listbox + public override string ToString() + { + return "Linedef is marked single-sided but has two sides"; + } + + // Rendering + public override void PlotSelection(IRenderer2D renderer) + { + renderer.PlotLinedef(line, General.Colors.Selection); + renderer.PlotVertex(line.Start, ColorCollection.VERTICES); + renderer.PlotVertex(line.End, ColorCollection.VERTICES); + } + + // Fix by flipping linedefs + public override bool Button1Click() + { + General.Map.UndoRedo.CreateUndo("Linedef flags change"); + line.ApplySidedFlags(); + General.Map.Map.Update(); + return true; + } + + // Fix by creating a sidedef + public override bool Button2Click() + { + General.Map.UndoRedo.CreateUndo("Remove back sidedef"); + line.Back.Dispose(); + line.ApplySidedFlags(); + General.Map.Map.Update(); + return true; + } + + #endregion + } +} diff --git a/Source/Plugins/BuilderModes/ErrorChecks/ResultLineOverlapping.cs b/Source/Plugins/BuilderModes/ErrorChecks/ResultLineOverlapping.cs new file mode 100644 index 0000000..d759259 --- /dev/null +++ b/Source/Plugins/BuilderModes/ErrorChecks/ResultLineOverlapping.cs @@ -0,0 +1,92 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using CodeImp.DoomBuilder.Windows; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Geometry; +using CodeImp.DoomBuilder.Editing; +using CodeImp.DoomBuilder.Actions; +using CodeImp.DoomBuilder.Types; +using CodeImp.DoomBuilder.Config; + +#endregion + +namespace CodeImp.DoomBuilder.BuilderModes +{ + public class ResultLineOverlapping : ErrorResult + { + #region ================== Variables + + private Linedef line1; + private Linedef line2; + + #endregion + + #region ================== Properties + + public override int Buttons { get { return 0; } } + + #endregion + + #region ================== Constructor / Destructor + + // Constructor + public ResultLineOverlapping(Linedef l1, Linedef l2) + { + // Initialize + this.line1 = l1; + this.line2 = l2; + this.viewobjects.Add(l1); + this.viewobjects.Add(l2); + this.description = "These linedefs are overlapping and they do not reference the same sector on all sides. Overlapping lines is only allowed when they reference the same sector on all sides."; + } + + #endregion + + #region ================== Methods + + // This must return the string that is displayed in the listbox + public override string ToString() + { + return "Linedefs are overlapping and references different sectors"; + } + + // Rendering + public override void PlotSelection(IRenderer2D renderer) + { + renderer.PlotLinedef(line1, General.Colors.Selection); + renderer.PlotLinedef(line2, General.Colors.Selection); + renderer.PlotVertex(line1.Start, ColorCollection.VERTICES); + renderer.PlotVertex(line1.End, ColorCollection.VERTICES); + renderer.PlotVertex(line2.Start, ColorCollection.VERTICES); + renderer.PlotVertex(line2.End, ColorCollection.VERTICES); + } + + #endregion + } +} diff --git a/Source/Plugins/BuilderModes/ErrorChecks/ResultNoErrors.cs b/Source/Plugins/BuilderModes/ErrorChecks/ResultNoErrors.cs new file mode 100644 index 0000000..8991ee4 --- /dev/null +++ b/Source/Plugins/BuilderModes/ErrorChecks/ResultNoErrors.cs @@ -0,0 +1,72 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using CodeImp.DoomBuilder.Windows; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Geometry; +using CodeImp.DoomBuilder.Editing; +using CodeImp.DoomBuilder.Actions; +using CodeImp.DoomBuilder.Types; +using CodeImp.DoomBuilder.Config; + +#endregion + +namespace CodeImp.DoomBuilder.BuilderModes +{ + public class ResultNoErrors : ErrorResult + { + #region ================== Variables + + #endregion + + #region ================== Properties + + public override int Buttons { get { return 0; } } + + #endregion + + #region ================== Constructor / Destructor + + // Constructor + public ResultNoErrors() + { + } + + #endregion + + #region ================== Methods + + // This must return the string that is displayed in the listbox + public override string ToString() + { + return "No errors were found."; + } + + #endregion + } +} diff --git a/Source/Plugins/BuilderModes/ErrorChecks/ResultSectorUnclosed.cs b/Source/Plugins/BuilderModes/ErrorChecks/ResultSectorUnclosed.cs new file mode 100644 index 0000000..ae0965c --- /dev/null +++ b/Source/Plugins/BuilderModes/ErrorChecks/ResultSectorUnclosed.cs @@ -0,0 +1,89 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using CodeImp.DoomBuilder.Windows; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Geometry; +using CodeImp.DoomBuilder.Editing; +using CodeImp.DoomBuilder.Actions; +using CodeImp.DoomBuilder.Types; +using CodeImp.DoomBuilder.Config; + +#endregion + +namespace CodeImp.DoomBuilder.BuilderModes +{ + public class ResultSectorUnclosed : ErrorResult + { + #region ================== Variables + + private Sector sector; + private List vertices; + private int index; + + #endregion + + #region ================== Properties + + #endregion + + #region ================== Constructor / Destructor + + // Constructor + public ResultSectorUnclosed(Sector s, List v) + { + // Initialize + this.sector = s; + this.vertices = new List(v); + this.viewobjects.Add(s); + foreach (Vertex vv in v) this.viewobjects.Add(vv); + this.description = "This sector is not a closed region and could cause problems with clipping and rendering in the game. The 'leaks' in the sector are indicated by the colored vertices."; + this.index = s.Index; + } + + #endregion + + #region ================== Methods + + // This must return the string that is displayed in the listbox + public override string ToString() + { + return "Sector " + index + " is not closed"; + } + + // Rendering + public override void PlotSelection(IRenderer2D renderer) + { + renderer.PlotSector(sector, General.Colors.Selection); + foreach (Vertex v in vertices) + renderer.PlotVertex(v, ColorCollection.SELECTION); + } + + #endregion + } +} diff --git a/Source/Plugins/BuilderModes/ErrorChecks/ResultStuckThingInLine.cs b/Source/Plugins/BuilderModes/ErrorChecks/ResultStuckThingInLine.cs new file mode 100644 index 0000000..75831aa --- /dev/null +++ b/Source/Plugins/BuilderModes/ErrorChecks/ResultStuckThingInLine.cs @@ -0,0 +1,91 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Rendering; + +#endregion + +namespace CodeImp.DoomBuilder.BuilderModes +{ + public class ResultStuckThingInLine : ErrorResult + { + #region ================== Variables + + private readonly Thing thing; + private readonly Linedef line; //mxd + + #endregion + + #region ================== Properties + + public override int Buttons { get { return 1; } } + public override string Button1Text { get { return "Delete Thing"; } } + + #endregion + + #region ================== Constructor / Destructor + + // Constructor + public ResultStuckThingInLine(Thing t, Linedef l) + { + // Initialize + thing = t; + line = l; //mxd + viewobjects.Add(t); + description = "This thing is stuck in a wall (single-sided line) and will fail to spawn in the map."; + } + + #endregion + + #region ================== Methods + + // This must return the string that is displayed in the listbox + public override string ToString() + { + return "Thing " + thing.Index + " (" + General.Map.Data.GetThingInfo(thing.Type).Title + ") is stuck in linedef " + line.Index + " at " + thing.Position.x + ", " + thing.Position.y; + } + + // Rendering + public override void RenderOverlaySelection(IRenderer2D renderer) + { + renderer.RenderThing(thing, renderer.DetermineThingColor(thing), 1.0f); + } + + // mxd. More rencering + public override void PlotSelection(IRenderer2D renderer) + { + renderer.PlotLinedef(line, General.Colors.Selection); + renderer.PlotVertex(line.Start, ColorCollection.VERTICES); + renderer.PlotVertex(line.End, ColorCollection.VERTICES); + } + + // This removes the thing + public override bool Button1Click() + { + General.Map.UndoRedo.CreateUndo("Delete thing"); + thing.Dispose(); + General.Map.IsChanged = true; + General.Map.ThingsFilter.Update(); + return true; + } + + #endregion + } +} diff --git a/Source/Plugins/BuilderModes/ErrorChecks/ResultStuckThingInThing.cs b/Source/Plugins/BuilderModes/ErrorChecks/ResultStuckThingInThing.cs new file mode 100644 index 0000000..f687488 --- /dev/null +++ b/Source/Plugins/BuilderModes/ErrorChecks/ResultStuckThingInThing.cs @@ -0,0 +1,96 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Net.NetworkInformation; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Rendering; + +#endregion + +namespace CodeImp.DoomBuilder.BuilderModes +{ + public class ResultStuckThingInThing : ErrorResult + { + #region ================== Variables + + private readonly Thing thing1; + private readonly Thing thing2; //mxd + + #endregion + + #region ================== Properties + + public override int Buttons { get { return 2; } } + public override string Button1Text { get { return "Delete 1st Thing"; } } + public override string Button2Text { get { return "Delete 2nd Thing"; } } //mxd + + #endregion + + #region ================== Constructor / Destructor + + // Constructor + public ResultStuckThingInThing(Thing t1, Thing t2) + { + // Initialize + this.thing1 = t1; + this.thing2 = t2; //mxd + this.viewobjects.Add(t1); + this.description = "This thing is stuck in another thing. The other thing will fail to spawn."; + } + + #endregion + + #region ================== Methods + + // This must return the string that is displayed in the listbox + public override string ToString() + { + return "Thing " + thing1.Index + " (" + General.Map.Data.GetThingInfo(thing1.Type).Title + ") is stuck in thing " + thing2.Index + " (" + General.Map.Data.GetThingInfo(thing2.Type).Title + ") at " + thing1.Position.x + ", " + thing1.Position.y; + } + + // Rendering + public override void RenderOverlaySelection(IRenderer2D renderer) + { + renderer.RenderThing(thing1, renderer.DetermineThingColor(thing1), 1.0f); + renderer.RenderThing(thing2, renderer.DetermineThingColor(thing2), 1.0f); + } + + // This removes the first thing + public override bool Button1Click() + { + General.Map.UndoRedo.CreateUndo("Delete thing"); + thing1.Dispose(); + General.Map.IsChanged = true; + General.Map.ThingsFilter.Update(); + return true; + } + + // This removes the thing + public override bool Button2Click() + { + General.Map.UndoRedo.CreateUndo("Delete thing"); + thing2.Dispose(); + General.Map.IsChanged = true; + General.Map.ThingsFilter.Update(); + return true; + } + + #endregion + } +} diff --git a/Source/Plugins/BuilderModes/ErrorChecks/ResultTextureMissing.cs b/Source/Plugins/BuilderModes/ErrorChecks/ResultTextureMissing.cs new file mode 100644 index 0000000..d31973b --- /dev/null +++ b/Source/Plugins/BuilderModes/ErrorChecks/ResultTextureMissing.cs @@ -0,0 +1,120 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using CodeImp.DoomBuilder.Windows; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Geometry; +using CodeImp.DoomBuilder.Editing; +using CodeImp.DoomBuilder.Actions; +using CodeImp.DoomBuilder.Types; +using CodeImp.DoomBuilder.Config; + +#endregion + +namespace CodeImp.DoomBuilder.BuilderModes +{ + public class ResultMissingTexture : ErrorResult + { + #region ================== Variables + + private Sidedef side; + private SidedefPart part; + + #endregion + + #region ================== Properties + + public override int Buttons { get { return 1; } } + public override string Button1Text { get { return "Add Default Texture"; } } + + #endregion + + #region ================== Constructor / Destructor + + // Constructor + public ResultMissingTexture(Sidedef sd, SidedefPart part) + { + // Initialize + this.side = sd; + this.part = part; + this.viewobjects.Add(sd); + this.description = "This sidedef is missing a texture where it is required and could cause a 'Hall Of Mirrors' visual problem in the map. Click the Add Default Texture button to add a texture to the line."; + } + + #endregion + + #region ================== Methods + + // This must return the string that is displayed in the listbox + public override string ToString() + { + string sidestr = side.IsFront ? "front" : "back"; + + switch (part) + { + case SidedefPart.Upper: + return "Sidedef has missing upper texture (" + sidestr + " side)"; + + case SidedefPart.Middle: + return "Sidedef has missing middle texture (" + sidestr + " side)"; + + case SidedefPart.Lower: + return "Sidedef has missing lower texture (" + sidestr + " side)"; + + default: + return "ERROR"; + } + } + + // Rendering + public override void PlotSelection(IRenderer2D renderer) + { + renderer.PlotLinedef(side.Line, General.Colors.Selection); + renderer.PlotVertex(side.Line.Start, ColorCollection.VERTICES); + renderer.PlotVertex(side.Line.End, ColorCollection.VERTICES); + } + + // Fix by setting default texture + public override bool Button1Click() + { + General.Map.UndoRedo.CreateUndo("Missing texture correction"); + General.Settings.FindDefaultDrawSettings(); + switch (part) + { + case SidedefPart.Upper: side.SetTextureHigh(General.Settings.DefaultTexture); break; + case SidedefPart.Middle: side.SetTextureMid(General.Settings.DefaultTexture); break; + case SidedefPart.Lower: side.SetTextureLow(General.Settings.DefaultTexture); break; + } + + General.Map.Map.Update(); + return true; + } + + #endregion + } +} diff --git a/Source/Plugins/BuilderModes/ErrorChecks/ResultThingOutside.cs b/Source/Plugins/BuilderModes/ErrorChecks/ResultThingOutside.cs new file mode 100644 index 0000000..e30254d --- /dev/null +++ b/Source/Plugins/BuilderModes/ErrorChecks/ResultThingOutside.cs @@ -0,0 +1,95 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using CodeImp.DoomBuilder.Windows; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Geometry; +using CodeImp.DoomBuilder.Editing; +using CodeImp.DoomBuilder.Actions; +using CodeImp.DoomBuilder.Types; +using CodeImp.DoomBuilder.Config; + +#endregion + +namespace CodeImp.DoomBuilder.BuilderModes +{ + public class ResultThingOutside : ErrorResult + { + #region ================== Variables + + private Thing thing; + + #endregion + + #region ================== Properties + + public override int Buttons { get { return 1; } } + public override string Button1Text { get { return "Delete Thing"; } } + + #endregion + + #region ================== Constructor / Destructor + + // Constructor + public ResultThingOutside(Thing t) + { + // Initialize + this.thing = t; + this.viewobjects.Add(t); + this.description = "This thing is completely outside the map."; + } + + #endregion + + #region ================== Methods + + // This must return the string that is displayed in the listbox + public override string ToString() + { + return General.Map.Data.GetThingInfo(thing.Type).Title + " is outside the map at " + thing.Position.x + ", " + thing.Position.y; + } + + // Rendering + public override void RenderOverlaySelection(IRenderer2D renderer) + { + renderer.RenderThing(thing, renderer.DetermineThingColor(thing), 1.0f); + } + + // This removes the thing + public override bool Button1Click() + { + General.Map.UndoRedo.CreateUndo("Delete thing"); + thing.Dispose(); + General.Map.IsChanged = true; + General.Map.ThingsFilter.Update(); + return true; + } + + #endregion + } +} diff --git a/Source/Plugins/BuilderModes/ErrorChecks/ResultUnknownFlat.cs b/Source/Plugins/BuilderModes/ErrorChecks/ResultUnknownFlat.cs new file mode 100644 index 0000000..3c487f0 --- /dev/null +++ b/Source/Plugins/BuilderModes/ErrorChecks/ResultUnknownFlat.cs @@ -0,0 +1,107 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using CodeImp.DoomBuilder.Windows; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Geometry; +using CodeImp.DoomBuilder.Editing; +using CodeImp.DoomBuilder.Actions; +using CodeImp.DoomBuilder.Types; +using CodeImp.DoomBuilder.Config; + +#endregion + +namespace CodeImp.DoomBuilder.BuilderModes +{ + public class ResultUnknownFlat : ErrorResult + { + #region ================== Variables + + private Sector sector; + private bool ceiling; + + #endregion + + #region ================== Properties + + public override int Buttons { get { return 1; } } + public override string Button1Text { get { return "Add Default Flat"; } } + + #endregion + + #region ================== Constructor / Destructor + + // Constructor + public ResultUnknownFlat(Sector s, bool ceiling) + { + // Initialize + this.sector = s; + this.ceiling = ceiling; + this.viewobjects.Add(s); + + string objname = ceiling ? "ceiling" : "floor"; + this.description = "This sector " + objname + " uses an unknown flat. This could be the result of missing resources, or a mistyped flat name. Click the Add Default Flat button to use a known flat instead."; + } + + #endregion + + #region ================== Methods + + // This must return the string that is displayed in the listbox + public override string ToString() + { + if (ceiling) + return "Sector has unknown ceiling flat \"" + sector.CeilTexture + "\""; + else + return "Sector has unknown floor flat \"" + sector.FloorTexture + "\""; + } + + // Rendering + public override void PlotSelection(IRenderer2D renderer) + { + renderer.PlotSector(sector, General.Colors.Selection); + } + + // Fix by setting default flat + public override bool Button1Click() + { + General.Map.UndoRedo.CreateUndo("Unknown flat correction"); + General.Settings.FindDefaultDrawSettings(); + + if (ceiling) + sector.SetCeilTexture(General.Settings.DefaultCeilingTexture); + else + sector.SetFloorTexture(General.Settings.DefaultFloorTexture); + + General.Map.Map.Update(); + return true; + } + + #endregion + } +} diff --git a/Source/Plugins/BuilderModes/ErrorChecks/ResultUnknownTexture.cs b/Source/Plugins/BuilderModes/ErrorChecks/ResultUnknownTexture.cs new file mode 100644 index 0000000..890fcdf --- /dev/null +++ b/Source/Plugins/BuilderModes/ErrorChecks/ResultUnknownTexture.cs @@ -0,0 +1,134 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using CodeImp.DoomBuilder.Windows; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Geometry; +using CodeImp.DoomBuilder.Editing; +using CodeImp.DoomBuilder.Actions; +using CodeImp.DoomBuilder.Types; +using CodeImp.DoomBuilder.Config; + +#endregion + +namespace CodeImp.DoomBuilder.BuilderModes +{ + public class ResultUnknownTexture : ErrorResult + { + #region ================== Variables + + private Sidedef side; + private SidedefPart part; + + #endregion + + #region ================== Properties + + public override int Buttons { get { return 2; } } + public override string Button1Text { get { return "Remove Texture"; } } + public override string Button2Text { get { return "Add Default Texture"; } } + + #endregion + + #region ================== Constructor / Destructor + + // Constructor + public ResultUnknownTexture(Sidedef sd, SidedefPart part) + { + // Initialize + this.side = sd; + this.part = part; + this.viewobjects.Add(sd); + this.description = "This sidedef uses an unknown texture. This could be the result of missing resources, or a mistyped texture name. Click the Remove Texture button to remove the texture or click on Add Default Texture to use a known texture instead."; + } + + #endregion + + #region ================== Methods + + // This must return the string that is displayed in the listbox + public override string ToString() + { + switch (part) + { + case SidedefPart.Upper: + return "Sidedef has unknown upper texture \"" + side.HighTexture + "\""; + + case SidedefPart.Middle: + return "Sidedef has unknown middle texture \"" + side.MiddleTexture + "\""; + + case SidedefPart.Lower: + return "Sidedef has unknown lower texture \"" + side.LowTexture + "\""; + + default: + return "ERROR"; + } + } + + // Rendering + public override void PlotSelection(IRenderer2D renderer) + { + renderer.PlotLinedef(side.Line, General.Colors.Selection); + renderer.PlotVertex(side.Line.Start, ColorCollection.VERTICES); + renderer.PlotVertex(side.Line.End, ColorCollection.VERTICES); + } + + // Fix by removing texture + public override bool Button1Click() + { + General.Map.UndoRedo.CreateUndo("Remove unknown texture"); + switch (part) + { + case SidedefPart.Upper: side.SetTextureHigh("-"); break; + case SidedefPart.Middle: side.SetTextureMid("-"); break; + case SidedefPart.Lower: side.SetTextureLow("-"); break; + } + + General.Map.Map.Update(); + return true; + } + + // Fix by setting default texture + public override bool Button2Click() + { + General.Map.UndoRedo.CreateUndo("Unknown texture correction"); + General.Settings.FindDefaultDrawSettings(); + switch (part) + { + case SidedefPart.Upper: side.SetTextureHigh(General.Settings.DefaultTexture); break; + case SidedefPart.Middle: side.SetTextureMid(General.Settings.DefaultTexture); break; + case SidedefPart.Lower: side.SetTextureLow(General.Settings.DefaultTexture); break; + } + + General.Map.Map.Update(); + return true; + } + + #endregion + } +} diff --git a/Source/Plugins/BuilderModes/FindReplace/FindAnyTextureFlat.cs b/Source/Plugins/BuilderModes/FindReplace/FindAnyTextureFlat.cs new file mode 100644 index 0000000..ab10097 --- /dev/null +++ b/Source/Plugins/BuilderModes/FindReplace/FindAnyTextureFlat.cs @@ -0,0 +1,227 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using CodeImp.DoomBuilder.Windows; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Geometry; +using System.Drawing; +using CodeImp.DoomBuilder.Editing; +using CodeImp.DoomBuilder.Config; + +#endregion + +namespace CodeImp.DoomBuilder.BuilderModes +{ + [FindReplace("Any Texture or Flat", BrowseButton = true)] + internal class FindAnyTextureFlat : FindReplaceType + { + #region ================== Constants + + #endregion + + #region ================== Variables + + #endregion + + #region ================== Properties + + #endregion + + #region ================== Constructor / Destructor + + // Constructor + public FindAnyTextureFlat() + { + // Initialize + + } + + // Destructor + ~FindAnyTextureFlat() + { + } + + #endregion + + #region ================== Methods + + // This is called to test if the item should be displayed + public override bool DetermineVisiblity() + { + return General.Map.Config.MixTexturesFlats; + } + + + // This is called when the browse button is pressed + public override string Browse(string initialvalue) + { + return General.Interface.BrowseTexture(BuilderPlug.Me.FindReplaceForm, initialvalue); + } + + + // This is called to perform a search (and replace) + // Returns a list of items to show in the results list + // replacewith is null when not replacing + public override FindReplaceObject[] Find(string value, bool withinselection, string replacewith, bool keepselection) + { + List objs = new List(); + + // Interpret the replacement + if (replacewith != null) + { + // If it cannot be interpreted, set replacewith to null (not replacing at all) + if (replacewith.Length < 0) replacewith = null; + if (replacewith.Length > 8) replacewith = null; + if (replacewith == null) + { + MessageBox.Show("Invalid replace value for this search type!", "Find and Replace", MessageBoxButtons.OK, MessageBoxIcon.Error); + return objs.ToArray(); + } + } + + // Interpret the find + long longfind = Lump.MakeLongName(value.Trim()); + + // Where to search? + ICollection seclist = withinselection ? General.Map.Map.GetSelectedSectors(true) : General.Map.Map.Sectors; + ICollection sidelist = withinselection ? General.Map.Map.GetSidedefsFromSelectedLinedefs(true) : General.Map.Map.Sidedefs; + + // Go for all sectors + foreach (Sector s in seclist) + { + // Flat matches? + if (s.LongCeilTexture == longfind) + { + // Replace and add to list + if (replacewith != null) s.SetCeilTexture(replacewith); + objs.Add(new FindReplaceObject(s, "Sector " + s.Index + " (ceiling)")); + } + + if (s.LongFloorTexture == longfind) + { + // Replace and add to list + if (replacewith != null) s.SetFloorTexture(replacewith); + objs.Add(new FindReplaceObject(s, "Sector " + s.Index + " (floor)")); + } + } + + // Go for all sidedefs + foreach (Sidedef sd in sidelist) + { + string side = sd.IsFront ? "front" : "back"; + + if (sd.LongHighTexture == longfind) + { + // Replace and add to list + if (replacewith != null) sd.SetTextureHigh(replacewith); + objs.Add(new FindReplaceObject(sd, "Sidedef " + sd.Index + " (" + side + ", high)")); + } + + if (sd.LongMiddleTexture == longfind) + { + // Replace and add to list + if (replacewith != null) sd.SetTextureMid(replacewith); + objs.Add(new FindReplaceObject(sd, "Sidedef " + sd.Index + " (" + side + ", middle)")); + } + + if (sd.LongLowTexture == longfind) + { + // Replace and add to list + if (replacewith != null) sd.SetTextureLow(replacewith); + objs.Add(new FindReplaceObject(sd, "Sidedef " + sd.Index + " (" + side + ", low)")); + } + } + + // When replacing, make sure we keep track of used textures + if (replacewith != null) General.Map.Data.UpdateUsedTextures(); + + return objs.ToArray(); + } + + // This is called when a specific object is selected from the list + public override void ObjectSelected(FindReplaceObject[] selection) + { + if (selection.Length == 1) + { + ZoomToSelection(selection); + if (selection[0].Object is Sector) + General.Interface.ShowSectorInfo(selection[0].Sector); + else if (selection[0].Object is Sidedef) + General.Interface.ShowLinedefInfo(selection[0].Sidedef.Line); + } + else + General.Interface.HideInfo(); + + General.Map.Map.ClearAllSelected(); + foreach (FindReplaceObject obj in selection) + { + if (obj.Object is Sector) + obj.Sector.Selected = true; + else if (obj.Object is Sidedef) + obj.Sidedef.Line.Selected = true; + } + } + + // Render selection + public override void PlotSelection(IRenderer2D renderer, FindReplaceObject[] selection) + { + foreach (FindReplaceObject o in selection) + { + if (o.Object is Sector) + { + foreach (Sidedef sd in o.Sector.Sidedefs) + { + renderer.PlotLinedef(sd.Line, General.Colors.Selection); + } + } + else if (o.Object is Sidedef) + { + renderer.PlotLinedef(o.Sidedef.Line, General.Colors.Selection); + } + } + } + + // Edit objects + public override void EditObjects(FindReplaceObject[] selection) + { + List sectors = new List(selection.Length); + List lines = new List(selection.Length); + foreach (FindReplaceObject o in selection) + { + if (o.Object is Sector) + sectors.Add(o.Sector); + else + lines.Add(o.Sidedef.Line); + } + if (sectors.Count > 0) General.Interface.ShowEditSectors(sectors); + if (lines.Count > 0) General.Interface.ShowEditLinedefs(lines); + } + + #endregion + } +} diff --git a/Source/Plugins/BuilderModes/FindReplace/FindLinedefNumber.cs b/Source/Plugins/BuilderModes/FindReplace/FindLinedefNumber.cs new file mode 100644 index 0000000..1e3daa7 --- /dev/null +++ b/Source/Plugins/BuilderModes/FindReplace/FindLinedefNumber.cs @@ -0,0 +1,139 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using CodeImp.DoomBuilder.Windows; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Geometry; +using System.Drawing; +using CodeImp.DoomBuilder.Editing; +using CodeImp.DoomBuilder.Config; + +#endregion + +namespace CodeImp.DoomBuilder.BuilderModes +{ + [FindReplace("Linedef Index", BrowseButton = false, Replacable = false)] + internal class FindLinedefNumber : FindReplaceType + { + #region ================== Constants + + #endregion + + #region ================== Variables + + #endregion + + #region ================== Properties + + #endregion + + #region ================== Constructor / Destructor + + // Constructor + public FindLinedefNumber() + { + // Initialize + + } + + // Destructor + ~FindLinedefNumber() + { + } + + #endregion + + #region ================== Methods + + // This is called when the browse button is pressed + public override string Browse(string initialvalue) + { + return ""; + } + + + // This is called to perform a search (and replace) + // Returns a list of items to show in the results list + // replacewith is null when not replacing + public override FindReplaceObject[] Find(string value, bool withinselection, string replacewith, bool keepselection) + { + List objs = new List(); + + // Interpret the number given + int index = 0; + if (int.TryParse(value, out index)) + { + Linedef l = General.Map.Map.GetLinedefByIndex(index); + if (l != null) + { + LinedefActionInfo info = General.Map.Config.GetLinedefActionInfo(l.Action); + if (!info.IsNull) + objs.Add(new FindReplaceObject(l, "Linedef " + index + " (" + info.Title + ")")); + else + objs.Add(new FindReplaceObject(l, "Linedef " + index)); + } + } + + return objs.ToArray(); + } + + // This is called when a specific object is selected from the list + public override void ObjectSelected(FindReplaceObject[] selection) + { + if (selection.Length == 1) + { + ZoomToSelection(selection); + General.Interface.ShowLinedefInfo(selection[0].Linedef); + } + else + General.Interface.HideInfo(); + + General.Map.Map.ClearAllSelected(); + foreach (FindReplaceObject obj in selection) obj.Linedef.Selected = true; + } + + // Render selection + public override void PlotSelection(IRenderer2D renderer, FindReplaceObject[] selection) + { + foreach (FindReplaceObject o in selection) + { + renderer.PlotLinedef(o.Linedef, General.Colors.Selection); + } + } + + // Edit objects + public override void EditObjects(FindReplaceObject[] selection) + { + List linedefs = new List(selection.Length); + foreach (FindReplaceObject o in selection) linedefs.Add(o.Linedef); + General.Interface.ShowEditLinedefs(linedefs); + } + + #endregion + } +} diff --git a/Source/Plugins/BuilderModes/FindReplace/FindLinedefSectorRef.cs b/Source/Plugins/BuilderModes/FindReplace/FindLinedefSectorRef.cs new file mode 100644 index 0000000..6e43f6d --- /dev/null +++ b/Source/Plugins/BuilderModes/FindReplace/FindLinedefSectorRef.cs @@ -0,0 +1,189 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using CodeImp.DoomBuilder.Windows; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Geometry; +using System.Drawing; +using CodeImp.DoomBuilder.Editing; +using CodeImp.DoomBuilder.Config; +using CodeImp.DoomBuilder.Types; + +#endregion + +namespace CodeImp.DoomBuilder.BuilderModes +{ + [FindReplace("Linedef Sector Reference", BrowseButton = false)] + internal class FindLinedefSectorRef : FindReplaceType + { + #region ================== Constants + + #endregion + + #region ================== Variables + + #endregion + + #region ================== Properties + + #endregion + + #region ================== Constructor / Destructor + + // Constructor + public FindLinedefSectorRef() + { + // Initialize + + } + + // Destructor + ~FindLinedefSectorRef() + { + } + + #endregion + + #region ================== Methods + + // This is called to test if the item should be displayed + public override bool DetermineVisiblity() + { + return General.Map.FormatInterface.HasActionArgs; + } + + + // This is called when the browse button is pressed + public override string Browse(string initialvalue) + { + return ""; + } + + + // This is called to perform a search (and replace) + // Returns a list of items to show in the results list + // replacewith is null when not replacing + public override FindReplaceObject[] Find(string value, bool withinselection, string replacewith, bool keepselection) + { + List objs = new List(); + + // Interpret the replacement + int replacetag = 0; + if (replacewith != null) + { + // If it cannot be interpreted, set replacewith to null (not replacing at all) + if (!int.TryParse(replacewith, out replacetag)) replacewith = null; + if (replacetag < 0) replacewith = null; + if (replacetag > 255) replacewith = null; + if (replacewith == null) + { + MessageBox.Show("Invalid replace value for this search type!", "Find and Replace", MessageBoxButtons.OK, MessageBoxIcon.Error); + return objs.ToArray(); + } + } + + // Interpret the number given + int tag = 0; + if (int.TryParse(value, out tag)) + { + // Where to search? + ICollection list = withinselection ? General.Map.Map.GetSelectedLinedefs(true) : General.Map.Map.Linedefs; + + // Go for all linedefs + foreach (Linedef l in list) + { + bool addline = false; + + LinedefActionInfo info = General.Map.Config.GetLinedefActionInfo(l.Action); + if (info.IsKnown && !info.IsNull) + { + // Go for all args + for (int i = 0; i < Linedef.NUM_ARGS; i++) + { + // Argument type matches? + if (info.Args[i].Used && (info.Args[i].Type == (int)UniversalType.SectorTag)) + { + if (l.Args[i] == tag) + { + // Replace + if (replacewith != null) l.Args[i] = replacetag; + addline = true; + } + } + } + } + + if (addline) + { + // Add to list + if (!info.IsNull) + objs.Add(new FindReplaceObject(l, "Linedef " + l.Index + " (" + info.Title + ")")); + else + objs.Add(new FindReplaceObject(l, "Linedef " + l.Index)); + } + } + } + + return objs.ToArray(); + } + + // This is called when a specific object is selected from the list + public override void ObjectSelected(FindReplaceObject[] selection) + { + if (selection.Length == 1) + { + ZoomToSelection(selection); + General.Interface.ShowLinedefInfo(selection[0].Linedef); + } + else + General.Interface.HideInfo(); + + General.Map.Map.ClearAllSelected(); + foreach (FindReplaceObject obj in selection) obj.Linedef.Selected = true; + } + + // Render selection + public override void PlotSelection(IRenderer2D renderer, FindReplaceObject[] selection) + { + foreach (FindReplaceObject o in selection) + { + renderer.PlotLinedef(o.Linedef, General.Colors.Selection); + } + } + + // Edit objects + public override void EditObjects(FindReplaceObject[] selection) + { + List lines = new List(selection.Length); + foreach (FindReplaceObject o in selection) lines.Add(o.Linedef); + General.Interface.ShowEditLinedefs(lines); + } + + #endregion + } +} diff --git a/Source/Plugins/BuilderModes/FindReplace/FindLinedefTags.cs b/Source/Plugins/BuilderModes/FindReplace/FindLinedefTags.cs new file mode 100644 index 0000000..6474d7c --- /dev/null +++ b/Source/Plugins/BuilderModes/FindReplace/FindLinedefTags.cs @@ -0,0 +1,172 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using CodeImp.DoomBuilder.Windows; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Geometry; +using System.Drawing; +using CodeImp.DoomBuilder.Editing; +using CodeImp.DoomBuilder.Config; + +#endregion + +namespace CodeImp.DoomBuilder.BuilderModes +{ + [FindReplace("Linedef Tags", BrowseButton = false)] + internal class FindLinedefTags : FindReplaceType + { + #region ================== Constants + + #endregion + + #region ================== Variables + + #endregion + + #region ================== Properties + + #endregion + + #region ================== Constructor / Destructor + + // Constructor + public FindLinedefTags() + { + // Initialize + + } + + // Destructor + ~FindLinedefTags() + { + } + + #endregion + + #region ================== Methods + + // This is called to test if the item should be displayed + public override bool DetermineVisiblity() + { + return General.Map.FormatInterface.HasLinedefTag; + } + + + // This is called when the browse button is pressed + public override string Browse(string initialvalue) + { + return ""; + } + + + // This is called to perform a search (and replace) + // Returns a list of items to show in the results list + // replacewith is null when not replacing + public override FindReplaceObject[] Find(string value, bool withinselection, string replacewith, bool keepselection) + { + List objs = new List(); + + // Interpret the replacement + int replacetag = 0; + if (replacewith != null) + { + // If it cannot be interpreted, set replacewith to null (not replacing at all) + if (!int.TryParse(replacewith, out replacetag)) replacewith = null; + if (replacetag < General.Map.FormatInterface.MinTag) replacewith = null; + if (replacetag > General.Map.FormatInterface.MaxTag) replacewith = null; + if (replacewith == null) + { + MessageBox.Show("Invalid replace value for this search type!", "Find and Replace", MessageBoxButtons.OK, MessageBoxIcon.Error); + return objs.ToArray(); + } + } + + // Interpret the number given + int tag = 0; + if (int.TryParse(value, out tag)) + { + // Where to search? + ICollection list = withinselection ? General.Map.Map.GetSelectedLinedefs(true) : General.Map.Map.Linedefs; + + // Go for all linedefs + foreach (Linedef l in list) + { + // Tag matches? + if (l.Tag == tag) + { + // Replace + if (replacewith != null) l.Tag = replacetag; + + // Add to list + LinedefActionInfo info = General.Map.Config.GetLinedefActionInfo(l.Action); + if (!info.IsNull) + objs.Add(new FindReplaceObject(l, "Linedef " + l.Index + " (" + info.Title + ")")); + else + objs.Add(new FindReplaceObject(l, "Linedef " + l.Index)); + } + } + } + + return objs.ToArray(); + } + + // This is called when a specific object is selected from the list + public override void ObjectSelected(FindReplaceObject[] selection) + { + if (selection.Length == 1) + { + ZoomToSelection(selection); + General.Interface.ShowLinedefInfo(selection[0].Linedef); + } + else + General.Interface.HideInfo(); + + General.Map.Map.ClearAllSelected(); + foreach (FindReplaceObject obj in selection) obj.Linedef.Selected = true; + } + + // Render selection + public override void PlotSelection(IRenderer2D renderer, FindReplaceObject[] selection) + { + foreach (FindReplaceObject o in selection) + { + renderer.PlotLinedef(o.Linedef, General.Colors.Selection); + } + } + + // Edit objects + public override void EditObjects(FindReplaceObject[] selection) + { + List lines = new List(selection.Length); + foreach (FindReplaceObject o in selection) lines.Add(o.Linedef); + General.Interface.ShowEditLinedefs(lines); + } + + #endregion + } +} diff --git a/Source/Plugins/BuilderModes/FindReplace/FindLinedefThingRef.cs b/Source/Plugins/BuilderModes/FindReplace/FindLinedefThingRef.cs new file mode 100644 index 0000000..0b35e0b --- /dev/null +++ b/Source/Plugins/BuilderModes/FindReplace/FindLinedefThingRef.cs @@ -0,0 +1,189 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using CodeImp.DoomBuilder.Windows; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Geometry; +using System.Drawing; +using CodeImp.DoomBuilder.Editing; +using CodeImp.DoomBuilder.Config; +using CodeImp.DoomBuilder.Types; + +#endregion + +namespace CodeImp.DoomBuilder.BuilderModes +{ + [FindReplace("Linedef Thing Reference", BrowseButton = false)] + internal class FindLinedefThingRef : FindReplaceType + { + #region ================== Constants + + #endregion + + #region ================== Variables + + #endregion + + #region ================== Properties + + #endregion + + #region ================== Constructor / Destructor + + // Constructor + public FindLinedefThingRef() + { + // Initialize + + } + + // Destructor + ~FindLinedefThingRef() + { + } + + #endregion + + #region ================== Methods + + // This is called to test if the item should be displayed + public override bool DetermineVisiblity() + { + return General.Map.FormatInterface.HasActionArgs; + } + + + // This is called when the browse button is pressed + public override string Browse(string initialvalue) + { + return ""; + } + + + // This is called to perform a search (and replace) + // Returns a list of items to show in the results list + // replacewith is null when not replacing + public override FindReplaceObject[] Find(string value, bool withinselection, string replacewith, bool keepselection) + { + List objs = new List(); + + // Interpret the replacement + int replacetag = 0; + if (replacewith != null) + { + // If it cannot be interpreted, set replacewith to null (not replacing at all) + if (!int.TryParse(replacewith, out replacetag)) replacewith = null; + if (replacetag < 0) replacewith = null; + if (replacetag > 255) replacewith = null; + if (replacewith == null) + { + MessageBox.Show("Invalid replace value for this search type!", "Find and Replace", MessageBoxButtons.OK, MessageBoxIcon.Error); + return objs.ToArray(); + } + } + + // Interpret the number given + int tag = 0; + if (int.TryParse(value, out tag)) + { + // Where to search? + ICollection list = withinselection ? General.Map.Map.GetSelectedLinedefs(true) : General.Map.Map.Linedefs; + + // Go for all linedefs + foreach (Linedef l in list) + { + bool addline = false; + + LinedefActionInfo info = General.Map.Config.GetLinedefActionInfo(l.Action); + if (info.IsKnown && !info.IsNull) + { + // Go for all args + for (int i = 0; i < Linedef.NUM_ARGS; i++) + { + // Argument type matches? + if (info.Args[i].Used && (info.Args[i].Type == (int)UniversalType.ThingTag)) + { + if (l.Args[i] == tag) + { + // Replace + if (replacewith != null) l.Args[i] = replacetag; + addline = true; + } + } + } + } + + if (addline) + { + // Add to list + if (!info.IsNull) + objs.Add(new FindReplaceObject(l, "Linedef " + l.Index + " (" + info.Title + ")")); + else + objs.Add(new FindReplaceObject(l, "Linedef " + l.Index)); + } + } + } + + return objs.ToArray(); + } + + // This is called when a specific object is selected from the list + public override void ObjectSelected(FindReplaceObject[] selection) + { + if (selection.Length == 1) + { + ZoomToSelection(selection); + General.Interface.ShowLinedefInfo(selection[0].Linedef); + } + else + General.Interface.HideInfo(); + + General.Map.Map.ClearAllSelected(); + foreach (FindReplaceObject obj in selection) obj.Linedef.Selected = true; + } + + // Render selection + public override void PlotSelection(IRenderer2D renderer, FindReplaceObject[] selection) + { + foreach (FindReplaceObject o in selection) + { + renderer.PlotLinedef(o.Linedef, General.Colors.Selection); + } + } + + // Edit objects + public override void EditObjects(FindReplaceObject[] selection) + { + List lines = new List(selection.Length); + foreach (FindReplaceObject o in selection) lines.Add(o.Linedef); + General.Interface.ShowEditLinedefs(lines); + } + + #endregion + } +} diff --git a/Source/Plugins/BuilderModes/FindReplace/FindLinedefTypes.cs b/Source/Plugins/BuilderModes/FindReplace/FindLinedefTypes.cs new file mode 100644 index 0000000..ae01a3f --- /dev/null +++ b/Source/Plugins/BuilderModes/FindReplace/FindLinedefTypes.cs @@ -0,0 +1,167 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using CodeImp.DoomBuilder.Windows; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Geometry; +using System.Drawing; +using CodeImp.DoomBuilder.Editing; +using CodeImp.DoomBuilder.Config; + +#endregion + +namespace CodeImp.DoomBuilder.BuilderModes +{ + [FindReplace("Linedef Actions", BrowseButton = true)] + internal class FindLinedefTypes : FindReplaceType + { + #region ================== Constants + + #endregion + + #region ================== Variables + + #endregion + + #region ================== Properties + + #endregion + + #region ================== Constructor / Destructor + + // Constructor + public FindLinedefTypes() + { + // Initialize + + } + + // Destructor + ~FindLinedefTypes() + { + } + + #endregion + + #region ================== Methods + + // This is called when the browse button is pressed + public override string Browse(string initialvalue) + { + int num = 0; + int.TryParse(initialvalue, out num); + return General.Interface.BrowseLinedefActions(BuilderPlug.Me.FindReplaceForm, num).ToString(); + } + + + // This is called to perform a search (and replace) + // Returns a list of items to show in the results list + // replacewith is null when not replacing + public override FindReplaceObject[] Find(string value, bool withinselection, string replacewith, bool keepselection) + { + List objs = new List(); + + // Interpret the replacement + int replaceaction = 0; + if (replacewith != null) + { + // If it cannot be interpreted, set replacewith to null (not replacing at all) + if (!int.TryParse(replacewith, out replaceaction)) replacewith = null; + if (replaceaction < 0) replacewith = null; + if (replaceaction > Int16.MaxValue) replacewith = null; + if (replacewith == null) + { + MessageBox.Show("Invalid replace value for this search type!", "Find and Replace", MessageBoxButtons.OK, MessageBoxIcon.Error); + return objs.ToArray(); + } + } + + // Interpret the number given + int action = 0; + if (int.TryParse(value, out action)) + { + // Where to search? + ICollection list = withinselection ? General.Map.Map.GetSelectedLinedefs(true) : General.Map.Map.Linedefs; + + // Go for all linedefs + foreach (Linedef l in list) + { + // Action matches? + if (l.Action == action) + { + // Replace + if (replacewith != null) l.Action = replaceaction; + + // Add to list + LinedefActionInfo info = General.Map.Config.GetLinedefActionInfo(l.Action); + if (!info.IsNull) + objs.Add(new FindReplaceObject(l, "Linedef " + l.Index + " (" + info.Title + ")")); + else + objs.Add(new FindReplaceObject(l, "Linedef " + l.Index)); + } + } + } + + return objs.ToArray(); + } + + // This is called when a specific object is selected from the list + public override void ObjectSelected(FindReplaceObject[] selection) + { + if (selection.Length == 1) + { + ZoomToSelection(selection); + General.Interface.ShowLinedefInfo(selection[0].Linedef); + } + else + General.Interface.HideInfo(); + + General.Map.Map.ClearAllSelected(); + foreach (FindReplaceObject obj in selection) obj.Linedef.Selected = true; + } + + // Render selection + public override void PlotSelection(IRenderer2D renderer, FindReplaceObject[] selection) + { + foreach (FindReplaceObject o in selection) + { + renderer.PlotLinedef(o.Linedef, General.Colors.Selection); + } + } + + // Edit objects + public override void EditObjects(FindReplaceObject[] selection) + { + List lines = new List(selection.Length); + foreach (FindReplaceObject o in selection) lines.Add(o.Linedef); + General.Interface.ShowEditLinedefs(lines); + } + + #endregion + } +} diff --git a/Source/Plugins/BuilderModes/FindReplace/FindReplaceAttribute.cs b/Source/Plugins/BuilderModes/FindReplace/FindReplaceAttribute.cs new file mode 100644 index 0000000..8bc5617 --- /dev/null +++ b/Source/Plugins/BuilderModes/FindReplace/FindReplaceAttribute.cs @@ -0,0 +1,80 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using CodeImp.DoomBuilder.Windows; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Geometry; +using System.Drawing; +using CodeImp.DoomBuilder.Editing; + +#endregion + +namespace CodeImp.DoomBuilder.BuilderModes +{ + [AttributeUsage(AttributeTargets.Class, Inherited = true, AllowMultiple = false)] + internal class FindReplaceAttribute : Attribute + { + #region ================== Variables + + private string displayname; + private bool browsebutton; + private bool replacable; + + #endregion + + #region ================== Properties + + public string DisplayName { get { return displayname; } set { displayname = value; } } + public bool BrowseButton { get { return browsebutton; } set { browsebutton = value; } } + public bool Replacable { get { return replacable; } set { replacable = value; } } + + #endregion + + #region ================== Constructor / Destructor + + // Constructor + public FindReplaceAttribute(string displayname) + { + // Initialize + this.displayname = displayname; + this.replacable = true; + } + + #endregion + + #region ================== Methods + + // String representation + public override string ToString() + { + return displayname; + } + + #endregion + } +} diff --git a/Source/Plugins/BuilderModes/FindReplace/FindReplaceObject.cs b/Source/Plugins/BuilderModes/FindReplace/FindReplaceObject.cs new file mode 100644 index 0000000..7dc43ba --- /dev/null +++ b/Source/Plugins/BuilderModes/FindReplace/FindReplaceObject.cs @@ -0,0 +1,120 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using CodeImp.DoomBuilder.Windows; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Geometry; +using System.Drawing; +using CodeImp.DoomBuilder.Editing; + +#endregion + +namespace CodeImp.DoomBuilder.BuilderModes +{ + internal class FindReplaceObject + { + #region ================== Variables + + private object obj; + private string title; + + #endregion + + #region ================== Properties + + public object Object { get { return obj; } set { obj = value; } } + public Sector Sector { get { return (Sector)obj; } } + public Linedef Linedef { get { return (Linedef)obj; } } + public Sidedef Sidedef { get { return (Sidedef)obj; } } + public Thing Thing { get { return (Thing)obj; } } + public Vertex Vertex { get { return (Vertex)obj; } } + public string Title { get { return title; } set { title = value; } } + + #endregion + + #region ================== Constructor / Destructor + + // Constructor + public FindReplaceObject(object obj, string title) + { + // Initialize + this.obj = obj; + this.title = title; + } + + #endregion + + #region ================== Methods + + // String representation + public override string ToString() + { + return title; + } + + // This adds the vertices of the object used for view area calculation + public void AddViewPoints(IList points) + { + if (obj is Vertex) + { + points.Add((obj as Vertex).Position); + } + else if (obj is Linedef) + { + points.Add((obj as Linedef).Start.Position); + points.Add((obj as Linedef).End.Position); + } + else if (obj is Sidedef) + { + points.Add((obj as Sidedef).Line.Start.Position); + points.Add((obj as Sidedef).Line.End.Position); + } + else if (obj is Sector) + { + Sector s = (obj as Sector); + foreach (Sidedef sd in s.Sidedefs) + { + points.Add(sd.Line.Start.Position); + points.Add(sd.Line.End.Position); + } + } + else if (obj is Thing) + { + Thing t = (obj as Thing); + Vector2D p = (Vector2D)t.Position; + points.Add(p); + points.Add(p + new Vector2D(t.Size * 2.0f, t.Size * 2.0f)); + points.Add(p + new Vector2D(t.Size * 2.0f, -t.Size * 2.0f)); + points.Add(p + new Vector2D(-t.Size * 2.0f, t.Size * 2.0f)); + points.Add(p + new Vector2D(-t.Size * 2.0f, -t.Size * 2.0f)); + } + } + + #endregion + } +} diff --git a/Source/Plugins/BuilderModes/FindReplace/FindReplaceType.cs b/Source/Plugins/BuilderModes/FindReplace/FindReplaceType.cs new file mode 100644 index 0000000..8a12527 --- /dev/null +++ b/Source/Plugins/BuilderModes/FindReplace/FindReplaceType.cs @@ -0,0 +1,170 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using CodeImp.DoomBuilder.Windows; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Geometry; +using System.Drawing; +using CodeImp.DoomBuilder.Editing; + +#endregion + +namespace CodeImp.DoomBuilder.BuilderModes +{ + internal class FindReplaceType + { + #region ================== Constants + + #endregion + + #region ================== Variables + + protected FindReplaceAttribute attribs; + + #endregion + + #region ================== Properties + + public FindReplaceAttribute Attributes { get { return attribs; } } + public bool AllowDelete { get { return false; } } + public virtual Presentation RenderPresentation { get { return Presentation.Standard; } } + + #endregion + + #region ================== Constructor / Destructor + + // Constructor + public FindReplaceType() + { + // Initialize + object[] attrs = this.GetType().GetCustomAttributes(typeof(FindReplaceAttribute), true); + attribs = (FindReplaceAttribute)attrs[0]; + } + + // Destructor + ~FindReplaceType() + { + } + + #endregion + + #region ================== Methods + + // This is called to test if the item should be displayed + public virtual bool DetermineVisiblity() + { + return true; + } + + // This is called when the browse button is pressed + public virtual string Browse(string initialvalue) + { + return ""; + } + + // This is called to perform a search (and replace) + // Must return a list of items to show in the results list + // replacewith is null when not replacing + public virtual FindReplaceObject[] Find(string value, bool withinselection, string replacewith, bool keepselection) + { + return new FindReplaceObject[0]; + } + + // String representation + public override string ToString() + { + return attribs.DisplayName; + } + + // This is called when a specific object is selected from the list + public virtual void ObjectSelected(FindReplaceObject[] selection) + { + } + + // This is called for rendering + public virtual void PlotSelection(IRenderer2D renderer, FindReplaceObject[] selection) + { + } + + // This is called for rendering + public virtual void RenderThingsSelection(IRenderer2D renderer, FindReplaceObject[] selection) + { + } + + // This is called for rendering + public virtual void RenderOverlaySelection(IRenderer2D renderer, FindReplaceObject[] selection) + { + } + + // This is called when the objects are to be edited + public virtual void EditObjects(FindReplaceObject[] selection) + { + } + + // This is called when the objects are to be deleted + public virtual void DeleteObjects(FindReplaceObject[] selection) + { + } + + // Call this to zoom in on the given selection + public virtual void ZoomToSelection(ICollection selection) + { + List points = new List(); + RectangleF area = MapSet.CreateEmptyArea(); + + // Add all points to a list + foreach (FindReplaceObject o in selection) o.AddViewPoints(points); + + // Make a view area from the points + foreach (Vector2D p in points) area = MapSet.IncreaseArea(area, p); + + // Make the area square, using the largest side + if (area.Width > area.Height) + { + float delta = area.Width - area.Height; + area.Y -= delta * 0.5f; + area.Height += delta; + } + else + { + float delta = area.Height - area.Width; + area.X -= delta * 0.5f; + area.Width += delta; + } + + // Add padding + area.Inflate(100f, 100f); + + // Zoom to area + ClassicMode editmode = (General.Editing.Mode as ClassicMode); + editmode.CenterOnArea(area, 0.6f); + } + + #endregion + } +} diff --git a/Source/Plugins/BuilderModes/FindReplace/FindSectorEffect.cs b/Source/Plugins/BuilderModes/FindReplace/FindSectorEffect.cs new file mode 100644 index 0000000..3697d6f --- /dev/null +++ b/Source/Plugins/BuilderModes/FindReplace/FindSectorEffect.cs @@ -0,0 +1,170 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using CodeImp.DoomBuilder.Windows; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Geometry; +using System.Drawing; +using CodeImp.DoomBuilder.Editing; +using CodeImp.DoomBuilder.Config; + +#endregion + +namespace CodeImp.DoomBuilder.BuilderModes +{ + [FindReplace("Sector Effect", BrowseButton = true)] + internal class FindSectorEffect : FindReplaceType + { + #region ================== Constants + + #endregion + + #region ================== Variables + + #endregion + + #region ================== Properties + + #endregion + + #region ================== Constructor / Destructor + + // Constructor + public FindSectorEffect() + { + // Initialize + + } + + // Destructor + ~FindSectorEffect() + { + } + + #endregion + + #region ================== Methods + + // This is called when the browse button is pressed + public override string Browse(string initialvalue) + { + int effect; + int.TryParse(initialvalue, out effect); + effect = General.Interface.BrowseSectorEffect(BuilderPlug.Me.FindReplaceForm, effect); + return effect.ToString(); + } + + + // This is called to perform a search (and replace) + // Returns a list of items to show in the results list + // replacewith is null when not replacing + public override FindReplaceObject[] Find(string value, bool withinselection, string replacewith, bool keepselection) + { + List objs = new List(); + + // Interpret the replacement + int replaceeffect = 0; + if (replacewith != null) + { + // If it cannot be interpreted, set replacewith to null (not replacing at all) + if (!int.TryParse(replacewith, out replaceeffect)) replacewith = null; + if (replaceeffect < 0) replacewith = null; + if (replaceeffect > Int16.MaxValue) replacewith = null; + if (replacewith == null) + { + MessageBox.Show("Invalid replace value for this search type!", "Find and Replace", MessageBoxButtons.OK, MessageBoxIcon.Error); + return objs.ToArray(); + } + } + + // Interpret the number given + int effect = 0; + if (int.TryParse(value, out effect)) + { + // Where to search? + ICollection list = withinselection ? General.Map.Map.GetSelectedSectors(true) : General.Map.Map.Sectors; + + // Go for all sectors + foreach (Sector s in list) + { + // Tag matches? + if (s.Effect == effect) + { + // Replace + if (replacewith != null) s.Effect = replaceeffect; + + SectorEffectInfo info = General.Map.Config.GetSectorEffectInfo(s.Effect); + if (!info.IsNull) + objs.Add(new FindReplaceObject(s, "Sector " + s.Index + " (" + info.Title + ")")); + else + objs.Add(new FindReplaceObject(s, "Sector " + s.Index)); + } + } + } + + return objs.ToArray(); + } + + // This is called when a specific object is selected from the list + public override void ObjectSelected(FindReplaceObject[] selection) + { + if (selection.Length == 1) + { + ZoomToSelection(selection); + General.Interface.ShowSectorInfo(selection[0].Sector); + } + else + General.Interface.HideInfo(); + + General.Map.Map.ClearAllSelected(); + foreach (FindReplaceObject obj in selection) obj.Sector.Selected = true; + } + + // Render selection + public override void PlotSelection(IRenderer2D renderer, FindReplaceObject[] selection) + { + foreach (FindReplaceObject o in selection) + { + foreach (Sidedef sd in o.Sector.Sidedefs) + { + renderer.PlotLinedef(sd.Line, General.Colors.Selection); + } + } + } + + // Edit objects + public override void EditObjects(FindReplaceObject[] selection) + { + List sectors = new List(selection.Length); + foreach (FindReplaceObject o in selection) sectors.Add(o.Sector); + General.Interface.ShowEditSectors(sectors); + } + + #endregion + } +} diff --git a/Source/Plugins/BuilderModes/FindReplace/FindSectorFlat.cs b/Source/Plugins/BuilderModes/FindReplace/FindSectorFlat.cs new file mode 100644 index 0000000..632b19e --- /dev/null +++ b/Source/Plugins/BuilderModes/FindReplace/FindSectorFlat.cs @@ -0,0 +1,168 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using CodeImp.DoomBuilder.Windows; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Geometry; +using System.Drawing; +using CodeImp.DoomBuilder.Editing; +using CodeImp.DoomBuilder.Config; + +#endregion + +namespace CodeImp.DoomBuilder.BuilderModes +{ + [FindReplace("Sector Flat", BrowseButton = true)] + internal class FindSectorFlat : FindReplaceType + { + #region ================== Constants + + #endregion + + #region ================== Variables + + #endregion + + #region ================== Properties + + #endregion + + #region ================== Constructor / Destructor + + // Constructor + public FindSectorFlat() + { + // Initialize + + } + + // Destructor + ~FindSectorFlat() + { + } + + #endregion + + #region ================== Methods + + // This is called when the browse button is pressed + public override string Browse(string initialvalue) + { + return General.Interface.BrowseFlat(BuilderPlug.Me.FindReplaceForm, initialvalue); + } + + + // This is called to perform a search (and replace) + // Returns a list of items to show in the results list + // replacewith is null when not replacing + public override FindReplaceObject[] Find(string value, bool withinselection, string replacewith, bool keepselection) + { + List objs = new List(); + + // Interpret the replacement + if (replacewith != null) + { + // If it cannot be interpreted, set replacewith to null (not replacing at all) + if (replacewith.Length < 0) replacewith = null; + if (replacewith.Length > 8) replacewith = null; + if (replacewith == null) + { + MessageBox.Show("Invalid replace value for this search type!", "Find and Replace", MessageBoxButtons.OK, MessageBoxIcon.Error); + return objs.ToArray(); + } + } + + // Interpret the find + long longfind = Lump.MakeLongName(value.Trim()); + + // Where to search? + ICollection list = withinselection ? General.Map.Map.GetSelectedSectors(true) : General.Map.Map.Sectors; + + // Go for all sectors + foreach (Sector s in list) + { + // Flat matches? + if (s.LongCeilTexture == longfind) + { + // Replace and add to list + if (replacewith != null) s.SetCeilTexture(replacewith); + objs.Add(new FindReplaceObject(s, "Sector " + s.Index + " (ceiling)")); + } + + if (s.LongFloorTexture == longfind) + { + // Replace and add to list + if (replacewith != null) s.SetFloorTexture(replacewith); + objs.Add(new FindReplaceObject(s, "Sector " + s.Index + " (floor)")); + } + } + + // When replacing, make sure we keep track of used textures + if (replacewith != null) General.Map.Data.UpdateUsedTextures(); + + return objs.ToArray(); + } + + // This is called when a specific object is selected from the list + public override void ObjectSelected(FindReplaceObject[] selection) + { + if (selection.Length == 1) + { + ZoomToSelection(selection); + General.Interface.ShowSectorInfo(selection[0].Sector); + } + else + General.Interface.HideInfo(); + + General.Map.Map.ClearAllSelected(); + foreach (FindReplaceObject obj in selection) obj.Sector.Selected = true; + } + + // Render selection + public override void PlotSelection(IRenderer2D renderer, FindReplaceObject[] selection) + { + foreach (FindReplaceObject o in selection) + { + foreach (Sidedef sd in o.Sector.Sidedefs) + { + renderer.PlotLinedef(sd.Line, General.Colors.Selection); + } + } + } + + // Edit objects + public override void EditObjects(FindReplaceObject[] selection) + { + List sectors = new List(selection.Length); + foreach (FindReplaceObject o in selection) sectors.Add(o.Sector); + General.Interface.ShowEditSectors(sectors); + } + + #endregion + } +} diff --git a/Source/Plugins/BuilderModes/FindReplace/FindSectorNumber.cs b/Source/Plugins/BuilderModes/FindReplace/FindSectorNumber.cs new file mode 100644 index 0000000..f9c9fa1 --- /dev/null +++ b/Source/Plugins/BuilderModes/FindReplace/FindSectorNumber.cs @@ -0,0 +1,142 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using CodeImp.DoomBuilder.Windows; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Geometry; +using System.Drawing; +using CodeImp.DoomBuilder.Editing; +using CodeImp.DoomBuilder.Config; + +#endregion + +namespace CodeImp.DoomBuilder.BuilderModes +{ + [FindReplace("Sector Index", BrowseButton = false, Replacable = false)] + internal class FindSectorNumber : FindReplaceType + { + #region ================== Constants + + #endregion + + #region ================== Variables + + #endregion + + #region ================== Properties + + #endregion + + #region ================== Constructor / Destructor + + // Constructor + public FindSectorNumber() + { + // Initialize + + } + + // Destructor + ~FindSectorNumber() + { + } + + #endregion + + #region ================== Methods + + // This is called when the browse button is pressed + public override string Browse(string initialvalue) + { + return ""; + } + + + // This is called to perform a search (and replace) + // Returns a list of items to show in the results list + // replacewith is null when not replacing + public override FindReplaceObject[] Find(string value, bool withinselection, string replacewith, bool keepselection) + { + List objs = new List(); + + // Interpret the number given + int index = 0; + if (int.TryParse(value, out index)) + { + Sector s = General.Map.Map.GetSectorByIndex(index); + if (s != null) + { + SectorEffectInfo info = General.Map.Config.GetSectorEffectInfo(s.Effect); + if (!info.IsNull) + objs.Add(new FindReplaceObject(s, "Sector " + index + " (" + info.Title + ")")); + else + objs.Add(new FindReplaceObject(s, "Sector " + index)); + } + } + + return objs.ToArray(); + } + + // This is called when a specific object is selected from the list + public override void ObjectSelected(FindReplaceObject[] selection) + { + if (selection.Length == 1) + { + ZoomToSelection(selection); + General.Interface.ShowSectorInfo(selection[0].Sector); + } + else + General.Interface.HideInfo(); + + General.Map.Map.ClearAllSelected(); + foreach (FindReplaceObject obj in selection) obj.Sector.Selected = true; + } + + // Render selection + public override void PlotSelection(IRenderer2D renderer, FindReplaceObject[] selection) + { + foreach (FindReplaceObject o in selection) + { + foreach (Sidedef sd in o.Sector.Sidedefs) + { + renderer.PlotLinedef(sd.Line, General.Colors.Selection); + } + } + } + + // Edit objects + public override void EditObjects(FindReplaceObject[] selection) + { + List sectors = new List(selection.Length); + foreach (FindReplaceObject o in selection) sectors.Add(o.Sector); + General.Interface.ShowEditSectors(sectors); + } + + #endregion + } +} diff --git a/Source/Plugins/BuilderModes/FindReplace/FindSectorTags.cs b/Source/Plugins/BuilderModes/FindReplace/FindSectorTags.cs new file mode 100644 index 0000000..12d3e44 --- /dev/null +++ b/Source/Plugins/BuilderModes/FindReplace/FindSectorTags.cs @@ -0,0 +1,167 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using CodeImp.DoomBuilder.Windows; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Geometry; +using System.Drawing; +using CodeImp.DoomBuilder.Editing; +using CodeImp.DoomBuilder.Config; + +#endregion + +namespace CodeImp.DoomBuilder.BuilderModes +{ + [FindReplace("Sector Tags", BrowseButton = false)] + internal class FindSectorTags : FindReplaceType + { + #region ================== Constants + + #endregion + + #region ================== Variables + + #endregion + + #region ================== Properties + + #endregion + + #region ================== Constructor / Destructor + + // Constructor + public FindSectorTags() + { + // Initialize + + } + + // Destructor + ~FindSectorTags() + { + } + + #endregion + + #region ================== Methods + + // This is called when the browse button is pressed + public override string Browse(string initialvalue) + { + return ""; + } + + + // This is called to perform a search (and replace) + // Returns a list of items to show in the results list + // replacewith is null when not replacing + public override FindReplaceObject[] Find(string value, bool withinselection, string replacewith, bool keepselection) + { + List objs = new List(); + + // Interpret the replacement + int replacetag = 0; + if (replacewith != null) + { + // If it cannot be interpreted, set replacewith to null (not replacing at all) + if (!int.TryParse(replacewith, out replacetag)) replacewith = null; + if (replacetag < General.Map.FormatInterface.MinTag) replacewith = null; + if (replacetag > General.Map.FormatInterface.MaxTag) replacewith = null; + if (replacewith == null) + { + MessageBox.Show("Invalid replace value for this search type!", "Find and Replace", MessageBoxButtons.OK, MessageBoxIcon.Error); + return objs.ToArray(); + } + } + + // Interpret the number given + int tag = 0; + if (int.TryParse(value, out tag)) + { + // Where to search? + ICollection list = withinselection ? General.Map.Map.GetSelectedSectors(true) : General.Map.Map.Sectors; + + // Go for all sectors + foreach (Sector s in list) + { + // Tag matches? + if (s.Tag == tag) + { + // Replace + if (replacewith != null) s.Tag = replacetag; + + SectorEffectInfo info = General.Map.Config.GetSectorEffectInfo(s.Effect); + if (!info.IsNull) + objs.Add(new FindReplaceObject(s, "Sector " + s.Index + " (" + info.Title + ")")); + else + objs.Add(new FindReplaceObject(s, "Sector " + s.Index)); + } + } + } + + return objs.ToArray(); + } + + // This is called when a specific object is selected from the list + public override void ObjectSelected(FindReplaceObject[] selection) + { + if (selection.Length == 1) + { + ZoomToSelection(selection); + General.Interface.ShowSectorInfo(selection[0].Sector); + } + else + General.Interface.HideInfo(); + + General.Map.Map.ClearAllSelected(); + foreach (FindReplaceObject obj in selection) obj.Sector.Selected = true; + } + + // Render selection + public override void PlotSelection(IRenderer2D renderer, FindReplaceObject[] selection) + { + foreach (FindReplaceObject o in selection) + { + foreach (Sidedef sd in o.Sector.Sidedefs) + { + renderer.PlotLinedef(sd.Line, General.Colors.Selection); + } + } + } + + // Edit objects + public override void EditObjects(FindReplaceObject[] selection) + { + List sectors = new List(selection.Length); + foreach (FindReplaceObject o in selection) sectors.Add(o.Sector); + General.Interface.ShowEditSectors(sectors); + } + + #endregion + } +} diff --git a/Source/Plugins/BuilderModes/FindReplace/FindSidedefNumber.cs b/Source/Plugins/BuilderModes/FindReplace/FindSidedefNumber.cs new file mode 100644 index 0000000..e346ba2 --- /dev/null +++ b/Source/Plugins/BuilderModes/FindReplace/FindSidedefNumber.cs @@ -0,0 +1,131 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using CodeImp.DoomBuilder.Windows; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Geometry; +using System.Drawing; +using CodeImp.DoomBuilder.Editing; + +#endregion + +namespace CodeImp.DoomBuilder.BuilderModes +{ + [FindReplace("Sidedef Index", BrowseButton = false, Replacable = false)] + internal class FindSidedefNumber : FindReplaceType + { + #region ================== Constants + + #endregion + + #region ================== Variables + + #endregion + + #region ================== Properties + + #endregion + + #region ================== Constructor / Destructor + + // Constructor + public FindSidedefNumber() + { + // Initialize + + } + + // Destructor + ~FindSidedefNumber() + { + } + + #endregion + + #region ================== Methods + + // This is called when the browse button is pressed + public override string Browse(string initialvalue) + { + return ""; + } + + + // This is called to perform a search (and replace) + // Returns a list of items to show in the results list + // replacewith is null when not replacing + public override FindReplaceObject[] Find(string value, bool withinselection, string replacewith, bool keepselection) + { + List objs = new List(); + + // Interpret the number given + int index = 0; + if (int.TryParse(value, out index)) + { + Sidedef sd = General.Map.Map.GetSidedefByIndex(index); + if (sd != null) objs.Add(new FindReplaceObject(sd, "Sidedef " + index)); + } + + return objs.ToArray(); + } + + // This is called when a specific object is selected from the list + public override void ObjectSelected(FindReplaceObject[] selection) + { + if (selection.Length == 1) + { + ZoomToSelection(selection); + General.Interface.ShowLinedefInfo(selection[0].Sidedef.Line); + } + else + General.Interface.HideInfo(); + + General.Map.Map.ClearAllSelected(); + foreach (FindReplaceObject obj in selection) obj.Sidedef.Line.Selected = true; + } + + // Render selection + public override void PlotSelection(IRenderer2D renderer, FindReplaceObject[] selection) + { + foreach (FindReplaceObject o in selection) + { + renderer.PlotLinedef(o.Sidedef.Line, General.Colors.Selection); + } + } + + // Edit objects + public override void EditObjects(FindReplaceObject[] selection) + { + List linedefs = new List(selection.Length); + foreach (FindReplaceObject o in selection) linedefs.Add(o.Sidedef.Line); + General.Interface.ShowEditLinedefs(linedefs); + } + + #endregion + } +} diff --git a/Source/Plugins/BuilderModes/FindReplace/FindSidedefTexture.cs b/Source/Plugins/BuilderModes/FindReplace/FindSidedefTexture.cs new file mode 100644 index 0000000..5cfb69f --- /dev/null +++ b/Source/Plugins/BuilderModes/FindReplace/FindSidedefTexture.cs @@ -0,0 +1,173 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using CodeImp.DoomBuilder.Windows; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Geometry; +using System.Drawing; +using CodeImp.DoomBuilder.Editing; +using CodeImp.DoomBuilder.Config; + +#endregion + +namespace CodeImp.DoomBuilder.BuilderModes +{ + [FindReplace("Sidedef Texture", BrowseButton = true)] + internal class FindSidedefTexture : FindReplaceType + { + #region ================== Constants + + #endregion + + #region ================== Variables + + #endregion + + #region ================== Properties + + #endregion + + #region ================== Constructor / Destructor + + // Constructor + public FindSidedefTexture() + { + // Initialize + + } + + // Destructor + ~FindSidedefTexture() + { + } + + #endregion + + #region ================== Methods + + // This is called when the browse button is pressed + public override string Browse(string initialvalue) + { + return General.Interface.BrowseTexture(BuilderPlug.Me.FindReplaceForm, initialvalue); + } + + + // This is called to perform a search (and replace) + // Returns a list of items to show in the results list + // replacewith is null when not replacing + public override FindReplaceObject[] Find(string value, bool withinselection, string replacewith, bool keepselection) + { + List objs = new List(); + + // Interpret the replacement + if (replacewith != null) + { + // If it cannot be interpreted, set replacewith to null (not replacing at all) + if (replacewith.Length < 0) replacewith = null; + if (replacewith.Length > 8) replacewith = null; + if (replacewith == null) + { + MessageBox.Show("Invalid replace value for this search type!", "Find and Replace", MessageBoxButtons.OK, MessageBoxIcon.Error); + return objs.ToArray(); + } + } + + // Interpret the find + long longfind = Lump.MakeLongName(value.Trim()); + + // Where to search? + ICollection list = withinselection ? General.Map.Map.GetSidedefsFromSelectedLinedefs(true) : General.Map.Map.Sidedefs; + + // Go for all sidedefs + foreach (Sidedef sd in list) + { + string side = sd.IsFront ? "front" : "back"; + + if (sd.LongHighTexture == longfind) + { + // Replace and add to list + if (replacewith != null) sd.SetTextureHigh(replacewith); + objs.Add(new FindReplaceObject(sd, "Sidedef " + sd.Index + " (" + side + ", high)")); + } + + if (sd.LongMiddleTexture == longfind) + { + // Replace and add to list + if (replacewith != null) sd.SetTextureMid(replacewith); + objs.Add(new FindReplaceObject(sd, "Sidedef " + sd.Index + " (" + side + ", middle)")); + } + + if (sd.LongLowTexture == longfind) + { + // Replace and add to list + if (replacewith != null) sd.SetTextureLow(replacewith); + objs.Add(new FindReplaceObject(sd, "Sidedef " + sd.Index + " (" + side + ", low)")); + } + } + + // When replacing, make sure we keep track of used textures + if (replacewith != null) General.Map.Data.UpdateUsedTextures(); + + return objs.ToArray(); + } + + // This is called when a specific object is selected from the list + public override void ObjectSelected(FindReplaceObject[] selection) + { + if (selection.Length == 1) + { + ZoomToSelection(selection); + General.Interface.ShowLinedefInfo(selection[0].Sidedef.Line); + } + else + General.Interface.HideInfo(); + + General.Map.Map.ClearAllSelected(); + foreach (FindReplaceObject obj in selection) obj.Sidedef.Line.Selected = true; + } + + // Render selection + public override void PlotSelection(IRenderer2D renderer, FindReplaceObject[] selection) + { + foreach (FindReplaceObject o in selection) + { + renderer.PlotLinedef(o.Sidedef.Line, General.Colors.Selection); + } + } + + // Edit objects + public override void EditObjects(FindReplaceObject[] selection) + { + List linedefs = new List(selection.Length); + foreach (FindReplaceObject o in selection) linedefs.Add(o.Sidedef.Line); + General.Interface.ShowEditLinedefs(linedefs); + } + + #endregion + } +} diff --git a/Source/Plugins/BuilderModes/FindReplace/FindThingAction.cs b/Source/Plugins/BuilderModes/FindReplace/FindThingAction.cs new file mode 100644 index 0000000..c354368 --- /dev/null +++ b/Source/Plugins/BuilderModes/FindReplace/FindThingAction.cs @@ -0,0 +1,174 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using CodeImp.DoomBuilder.Windows; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Geometry; +using System.Drawing; +using CodeImp.DoomBuilder.Editing; +using CodeImp.DoomBuilder.Config; + +#endregion + +namespace CodeImp.DoomBuilder.BuilderModes +{ + [FindReplace("Thing Action", BrowseButton = true)] + internal class FindThingAction : FindReplaceType + { + #region ================== Constants + + #endregion + + #region ================== Variables + + #endregion + + #region ================== Properties + + public override Presentation RenderPresentation { get { return Presentation.Things; } } + + #endregion + + #region ================== Constructor / Destructor + + // Constructor + public FindThingAction() + { + // Initialize + + } + + // Destructor + ~FindThingAction() + { + } + + #endregion + + #region ================== Methods + + // This is called to test if the item should be displayed + public override bool DetermineVisiblity() + { + return General.Map.FormatInterface.HasThingAction; + } + + + // This is called when the browse button is pressed + public override string Browse(string initialvalue) + { + int action; + int.TryParse(initialvalue, out action); + action = General.Interface.BrowseLinedefActions(BuilderPlug.Me.FindReplaceForm, action); + return action.ToString(); + } + + + // This is called to perform a search (and replace) + // Returns a list of items to show in the results list + // replacewith is null when not replacing + public override FindReplaceObject[] Find(string value, bool withinselection, string replacewith, bool keepselection) + { + List objs = new List(); + + // Interpret the replacement + int replaceaction = 0; + if (replacewith != null) + { + // If it cannot be interpreted, set replacewith to null (not replacing at all) + if (!int.TryParse(replacewith, out replaceaction)) replacewith = null; + if (replaceaction < 0) replacewith = null; + if (replaceaction > Int16.MaxValue) replacewith = null; + if (replacewith == null) + { + MessageBox.Show("Invalid replace value for this search type!", "Find and Replace", MessageBoxButtons.OK, MessageBoxIcon.Error); + return objs.ToArray(); + } + } + + // Interpret the number given + int findaction = 0; + if (int.TryParse(value, out findaction)) + { + // Where to search? + ICollection list = withinselection ? General.Map.Map.GetSelectedThings(true) : General.Map.Map.Things; + + // Go for all things + foreach (Thing t in list) + { + // Match? + if (t.Action == findaction) + { + // Replace + if (replacewith != null) t.Action = replaceaction; + + // Add to list + ThingTypeInfo ti = General.Map.Data.GetThingInfo(t.Type); + objs.Add(new FindReplaceObject(t, "Thing " + t.Index + " (" + ti.Title + ")")); + } + } + } + + return objs.ToArray(); + } + + // This is called when a specific object is selected from the list + public override void ObjectSelected(FindReplaceObject[] selection) + { + if (selection.Length == 1) + { + ZoomToSelection(selection); + General.Interface.ShowThingInfo(selection[0].Thing); + } + else + General.Interface.HideInfo(); + + General.Map.Map.ClearAllSelected(); + foreach (FindReplaceObject obj in selection) obj.Thing.Selected = true; + } + + // Render selection + public override void RenderThingsSelection(IRenderer2D renderer, FindReplaceObject[] selection) + { + foreach (FindReplaceObject o in selection) + { + renderer.RenderThing(o.Thing, General.Colors.Selection, 1.0f); + } + } + + // Edit objects + public override void EditObjects(FindReplaceObject[] selection) + { + List things = new List(selection.Length); + foreach (FindReplaceObject o in selection) things.Add(o.Thing); + General.Interface.ShowEditThings(things); + } + + #endregion + } +} diff --git a/Source/Plugins/BuilderModes/FindReplace/FindThingNumber.cs b/Source/Plugins/BuilderModes/FindReplace/FindThingNumber.cs new file mode 100644 index 0000000..73594a2 --- /dev/null +++ b/Source/Plugins/BuilderModes/FindReplace/FindThingNumber.cs @@ -0,0 +1,138 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using CodeImp.DoomBuilder.Windows; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Geometry; +using System.Drawing; +using CodeImp.DoomBuilder.Editing; +using CodeImp.DoomBuilder.Config; + +#endregion + +namespace CodeImp.DoomBuilder.BuilderModes +{ + [FindReplace("Thing Index", BrowseButton = false, Replacable = false)] + internal class FindThingNumber : FindReplaceType + { + #region ================== Constants + + #endregion + + #region ================== Variables + + #endregion + + #region ================== Properties + + public override Presentation RenderPresentation { get { return Presentation.Things; } } + + #endregion + + #region ================== Constructor / Destructor + + // Constructor + public FindThingNumber() + { + // Initialize + + } + + // Destructor + ~FindThingNumber() + { + } + + #endregion + + #region ================== Methods + + // This is called when the browse button is pressed + public override string Browse(string initialvalue) + { + return ""; + } + + + // This is called to perform a search (and replace) + // Returns a list of items to show in the results list + // replacewith is null when not replacing + public override FindReplaceObject[] Find(string value, bool withinselection, string replacewith, bool keepselection) + { + List objs = new List(); + + // Interpret the number given + int index = 0; + if (int.TryParse(value, out index)) + { + Thing t = General.Map.Map.GetThingByIndex(index); + if (t != null) + { + ThingTypeInfo ti = General.Map.Data.GetThingInfo(t.Type); + objs.Add(new FindReplaceObject(t, "Thing " + index + " (" + ti.Title + ")")); + } + } + + return objs.ToArray(); + } + + // This is called when a specific object is selected from the list + public override void ObjectSelected(FindReplaceObject[] selection) + { + if (selection.Length == 1) + { + ZoomToSelection(selection); + General.Interface.ShowThingInfo(selection[0].Thing); + } + else + General.Interface.HideInfo(); + + General.Map.Map.ClearAllSelected(); + foreach (FindReplaceObject obj in selection) obj.Thing.Selected = true; + } + + // Render selection + public override void RenderThingsSelection(IRenderer2D renderer, FindReplaceObject[] selection) + { + foreach (FindReplaceObject o in selection) + { + renderer.RenderThing(o.Thing, General.Colors.Selection, 1.0f); + } + } + + // Edit objects + public override void EditObjects(FindReplaceObject[] selection) + { + List things = new List(selection.Length); + foreach (FindReplaceObject o in selection) things.Add(o.Thing); + General.Interface.ShowEditThings(things); + } + + #endregion + } +} diff --git a/Source/Plugins/BuilderModes/FindReplace/FindThingSectorRef.cs b/Source/Plugins/BuilderModes/FindReplace/FindThingSectorRef.cs new file mode 100644 index 0000000..9c1b915 --- /dev/null +++ b/Source/Plugins/BuilderModes/FindReplace/FindThingSectorRef.cs @@ -0,0 +1,189 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using CodeImp.DoomBuilder.Windows; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Geometry; +using System.Drawing; +using CodeImp.DoomBuilder.Editing; +using CodeImp.DoomBuilder.Config; +using CodeImp.DoomBuilder.Types; + +#endregion + +namespace CodeImp.DoomBuilder.BuilderModes +{ + [FindReplace("Thing Sector Reference", BrowseButton = false)] + internal class FindThingSectorRef : FindReplaceType + { + #region ================== Constants + + #endregion + + #region ================== Variables + + #endregion + + #region ================== Properties + + public override Presentation RenderPresentation { get { return Presentation.Things; } } + + #endregion + + #region ================== Constructor / Destructor + + // Constructor + public FindThingSectorRef() + { + // Initialize + + } + + // Destructor + ~FindThingSectorRef() + { + } + + #endregion + + #region ================== Methods + + // This is called to test if the item should be displayed + public override bool DetermineVisiblity() + { + return General.Map.FormatInterface.HasThingAction && General.Map.FormatInterface.HasActionArgs; + } + + + // This is called when the browse button is pressed + public override string Browse(string initialvalue) + { + return ""; + } + + + // This is called to perform a search (and replace) + // Returns a list of items to show in the results list + // replacewith is null when not replacing + public override FindReplaceObject[] Find(string value, bool withinselection, string replacewith, bool keepselection) + { + List objs = new List(); + + // Interpret the replacement + int replacetag = 0; + if (replacewith != null) + { + // If it cannot be interpreted, set replacewith to null (not replacing at all) + if (!int.TryParse(replacewith, out replacetag)) replacewith = null; + if (replacetag < 0) replacewith = null; + if (replacetag > 255) replacewith = null; + if (replacewith == null) + { + MessageBox.Show("Invalid replace value for this search type!", "Find and Replace", MessageBoxButtons.OK, MessageBoxIcon.Error); + return objs.ToArray(); + } + } + + // Interpret the number given + int tag = 0; + if (int.TryParse(value, out tag)) + { + // Where to search? + ICollection list = withinselection ? General.Map.Map.GetSelectedThings(true) : General.Map.Map.Things; + + // Go for all things + foreach (Thing t in list) + { + bool addthing = false; + + LinedefActionInfo info = General.Map.Config.GetLinedefActionInfo(t.Action); + if (info.IsKnown && !info.IsNull) + { + // Go for all args + for (int i = 0; i < Linedef.NUM_ARGS; i++) + { + // Argument type matches? + if (info.Args[i].Used && (info.Args[i].Type == (int)UniversalType.SectorTag)) + { + if (t.Args[i] == tag) + { + // Replace + if (replacewith != null) t.Args[i] = replacetag; + addthing = true; + } + } + } + } + + if (addthing) + { + // Add to list + ThingTypeInfo ti = General.Map.Data.GetThingInfo(t.Type); + objs.Add(new FindReplaceObject(t, "Thing " + t.Index + " (" + ti.Title + ")")); + } + } + } + + return objs.ToArray(); + } + + // This is called when a specific object is selected from the list + public override void ObjectSelected(FindReplaceObject[] selection) + { + if (selection.Length == 1) + { + ZoomToSelection(selection); + General.Interface.ShowThingInfo(selection[0].Thing); + } + else + General.Interface.HideInfo(); + + General.Map.Map.ClearAllSelected(); + foreach (FindReplaceObject obj in selection) obj.Thing.Selected = true; + } + + // Render selection + public override void RenderThingsSelection(IRenderer2D renderer, FindReplaceObject[] selection) + { + foreach (FindReplaceObject o in selection) + { + renderer.RenderThing(o.Thing, General.Colors.Selection, 1.0f); + } + } + + // Edit objects + public override void EditObjects(FindReplaceObject[] selection) + { + List things = new List(selection.Length); + foreach (FindReplaceObject o in selection) things.Add(o.Thing); + General.Interface.ShowEditThings(things); + } + + #endregion + } +} diff --git a/Source/Plugins/BuilderModes/FindReplace/FindThingTag.cs b/Source/Plugins/BuilderModes/FindReplace/FindThingTag.cs new file mode 100644 index 0000000..92a9f41 --- /dev/null +++ b/Source/Plugins/BuilderModes/FindReplace/FindThingTag.cs @@ -0,0 +1,171 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using CodeImp.DoomBuilder.Windows; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Geometry; +using System.Drawing; +using CodeImp.DoomBuilder.Editing; +using CodeImp.DoomBuilder.Config; + +#endregion + +namespace CodeImp.DoomBuilder.BuilderModes +{ + [FindReplace("Thing Tag", BrowseButton = false)] + internal class FindThingTag : FindReplaceType + { + #region ================== Constants + + #endregion + + #region ================== Variables + + #endregion + + #region ================== Properties + + public override Presentation RenderPresentation { get { return Presentation.Things; } } + + #endregion + + #region ================== Constructor / Destructor + + // Constructor + public FindThingTag() + { + // Initialize + + } + + // Destructor + ~FindThingTag() + { + } + + #endregion + + #region ================== Methods + + // This is called to test if the item should be displayed + public override bool DetermineVisiblity() + { + return General.Map.FormatInterface.HasThingTag; + } + + + // This is called when the browse button is pressed + public override string Browse(string initialvalue) + { + return ""; + } + + + // This is called to perform a search (and replace) + // Returns a list of items to show in the results list + // replacewith is null when not replacing + public override FindReplaceObject[] Find(string value, bool withinselection, string replacewith, bool keepselection) + { + List objs = new List(); + + // Interpret the replacement + int replacetag = 0; + if (replacewith != null) + { + // If it cannot be interpreted, set replacewith to null (not replacing at all) + if (!int.TryParse(replacewith, out replacetag)) replacewith = null; + if (replacetag < 0) replacewith = null; + if (replacetag > Int16.MaxValue) replacewith = null; + if (replacewith == null) + { + MessageBox.Show("Invalid replace value for this search type!", "Find and Replace", MessageBoxButtons.OK, MessageBoxIcon.Error); + return objs.ToArray(); + } + } + + // Interpret the number given + int tag = 0; + if (int.TryParse(value, out tag)) + { + // Where to search? + ICollection list = withinselection ? General.Map.Map.GetSelectedThings(true) : General.Map.Map.Things; + + // Go for all things + foreach (Thing t in list) + { + // Match? + if (t.Tag == tag) + { + // Replace + if (replacewith != null) t.Tag = replacetag; + + // Add to list + ThingTypeInfo ti = General.Map.Data.GetThingInfo(t.Type); + objs.Add(new FindReplaceObject(t, "Thing " + t.Index + " (" + ti.Title + ")")); + } + } + } + + return objs.ToArray(); + } + + // This is called when a specific object is selected from the list + public override void ObjectSelected(FindReplaceObject[] selection) + { + if (selection.Length == 1) + { + ZoomToSelection(selection); + General.Interface.ShowThingInfo(selection[0].Thing); + } + else + General.Interface.HideInfo(); + + General.Map.Map.ClearAllSelected(); + foreach (FindReplaceObject obj in selection) obj.Thing.Selected = true; + } + + // Render selection + public override void RenderThingsSelection(IRenderer2D renderer, FindReplaceObject[] selection) + { + foreach (FindReplaceObject o in selection) + { + renderer.RenderThing(o.Thing, General.Colors.Selection, 1.0f); + } + } + + // Edit objects + public override void EditObjects(FindReplaceObject[] selection) + { + List things = new List(selection.Length); + foreach (FindReplaceObject o in selection) things.Add(o.Thing); + General.Interface.ShowEditThings(things); + } + + #endregion + } +} diff --git a/Source/Plugins/BuilderModes/FindReplace/FindThingThingRef.cs b/Source/Plugins/BuilderModes/FindReplace/FindThingThingRef.cs new file mode 100644 index 0000000..b8ec926 --- /dev/null +++ b/Source/Plugins/BuilderModes/FindReplace/FindThingThingRef.cs @@ -0,0 +1,189 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using CodeImp.DoomBuilder.Windows; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Geometry; +using System.Drawing; +using CodeImp.DoomBuilder.Editing; +using CodeImp.DoomBuilder.Config; +using CodeImp.DoomBuilder.Types; + +#endregion + +namespace CodeImp.DoomBuilder.BuilderModes +{ + [FindReplace("Thing Thing Reference", BrowseButton = false)] + internal class FindThingThingRef : FindReplaceType + { + #region ================== Constants + + #endregion + + #region ================== Variables + + #endregion + + #region ================== Properties + + public override Presentation RenderPresentation { get { return Presentation.Things; } } + + #endregion + + #region ================== Constructor / Destructor + + // Constructor + public FindThingThingRef() + { + // Initialize + + } + + // Destructor + ~FindThingThingRef() + { + } + + #endregion + + #region ================== Methods + + // This is called to test if the item should be displayed + public override bool DetermineVisiblity() + { + return General.Map.FormatInterface.HasThingAction && General.Map.FormatInterface.HasThingTag; + } + + + // This is called when the browse button is pressed + public override string Browse(string initialvalue) + { + return ""; + } + + + // This is called to perform a search (and replace) + // Returns a list of items to show in the results list + // replacewith is null when not replacing + public override FindReplaceObject[] Find(string value, bool withinselection, string replacewith, bool keepselection) + { + List objs = new List(); + + // Interpret the replacement + int replacetag = 0; + if (replacewith != null) + { + // If it cannot be interpreted, set replacewith to null (not replacing at all) + if (!int.TryParse(replacewith, out replacetag)) replacewith = null; + if (replacetag < 0) replacewith = null; + if (replacetag > 255) replacewith = null; + if (replacewith == null) + { + MessageBox.Show("Invalid replace value for this search type!", "Find and Replace", MessageBoxButtons.OK, MessageBoxIcon.Error); + return objs.ToArray(); + } + } + + // Interpret the number given + int tag = 0; + if (int.TryParse(value, out tag)) + { + // Where to search? + ICollection list = withinselection ? General.Map.Map.GetSelectedThings(true) : General.Map.Map.Things; + + // Go for all things + foreach (Thing t in list) + { + bool addthing = false; + + LinedefActionInfo info = General.Map.Config.GetLinedefActionInfo(t.Action); + if (info.IsKnown && !info.IsNull) + { + // Go for all args + for (int i = 0; i < Linedef.NUM_ARGS; i++) + { + // Argument type matches? + if (info.Args[i].Used && (info.Args[i].Type == (int)UniversalType.ThingTag)) + { + if (t.Args[i] == tag) + { + // Replace + if (replacewith != null) t.Args[i] = replacetag; + addthing = true; + } + } + } + } + + if (addthing) + { + // Add to list + ThingTypeInfo ti = General.Map.Data.GetThingInfo(t.Type); + objs.Add(new FindReplaceObject(t, "Thing " + t.Index + " (" + ti.Title + ")")); + } + } + } + + return objs.ToArray(); + } + + // This is called when a specific object is selected from the list + public override void ObjectSelected(FindReplaceObject[] selection) + { + if (selection.Length == 1) + { + ZoomToSelection(selection); + General.Interface.ShowThingInfo(selection[0].Thing); + } + else + General.Interface.HideInfo(); + + General.Map.Map.ClearAllSelected(); + foreach (FindReplaceObject obj in selection) obj.Thing.Selected = true; + } + + // Render selection + public override void RenderThingsSelection(IRenderer2D renderer, FindReplaceObject[] selection) + { + foreach (FindReplaceObject o in selection) + { + renderer.RenderThing(o.Thing, General.Colors.Selection, 1.0f); + } + } + + // Edit objects + public override void EditObjects(FindReplaceObject[] selection) + { + List things = new List(selection.Length); + foreach (FindReplaceObject o in selection) things.Add(o.Thing); + General.Interface.ShowEditThings(things); + } + + #endregion + } +} diff --git a/Source/Plugins/BuilderModes/FindReplace/FindThingType.cs b/Source/Plugins/BuilderModes/FindReplace/FindThingType.cs new file mode 100644 index 0000000..757389d --- /dev/null +++ b/Source/Plugins/BuilderModes/FindReplace/FindThingType.cs @@ -0,0 +1,171 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using CodeImp.DoomBuilder.Windows; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Geometry; +using System.Drawing; +using CodeImp.DoomBuilder.Editing; +using CodeImp.DoomBuilder.Config; + +#endregion + +namespace CodeImp.DoomBuilder.BuilderModes +{ + [FindReplace("Thing Type", BrowseButton = true)] + internal class FindThingType : FindReplaceType + { + #region ================== Constants + + #endregion + + #region ================== Variables + + #endregion + + #region ================== Properties + + public override Presentation RenderPresentation { get { return Presentation.Things; } } + + #endregion + + #region ================== Constructor / Destructor + + // Constructor + public FindThingType() + { + // Initialize + + } + + // Destructor + ~FindThingType() + { + } + + #endregion + + #region ================== Methods + + // This is called when the browse button is pressed + public override string Browse(string initialvalue) + { + int type; + int.TryParse(initialvalue, out type); + type = General.Interface.BrowseThingType(BuilderPlug.Me.FindReplaceForm, type); + return type.ToString(); + } + + + // This is called to perform a search (and replace) + // Returns a list of items to show in the results list + // replacewith is null when not replacing + public override FindReplaceObject[] Find(string value, bool withinselection, string replacewith, bool keepselection) + { + List objs = new List(); + + // Interpret the replacement + int replacetype = 0; + if (replacewith != null) + { + // If it cannot be interpreted, set replacewith to null (not replacing at all) + if (!int.TryParse(replacewith, out replacetype)) replacewith = null; + if (replacetype < 0) replacewith = null; + if (replacetype > Int16.MaxValue) replacewith = null; + if (replacewith == null) + { + MessageBox.Show("Invalid replace value for this search type!", "Find and Replace", MessageBoxButtons.OK, MessageBoxIcon.Error); + return objs.ToArray(); + } + } + + // Interpret the number given + int findtype = 0; + if (int.TryParse(value, out findtype)) + { + // Where to search? + ICollection list = withinselection ? General.Map.Map.GetSelectedThings(true) : General.Map.Map.Things; + + // Go for all things + foreach (Thing t in list) + { + // Match? + if (t.Type == findtype) + { + // Replace + if (replacewith != null) + { + t.Type = replacetype; + t.UpdateConfiguration(); + } + + // Add to list + ThingTypeInfo ti = General.Map.Data.GetThingInfo(t.Type); + objs.Add(new FindReplaceObject(t, "Thing " + t.Index + " (" + ti.Title + ")")); + } + } + } + + return objs.ToArray(); + } + + // This is called when a specific object is selected from the list + public override void ObjectSelected(FindReplaceObject[] selection) + { + if (selection.Length == 1) + { + ZoomToSelection(selection); + General.Interface.ShowThingInfo(selection[0].Thing); + } + else + General.Interface.HideInfo(); + + General.Map.Map.ClearAllSelected(); + foreach (FindReplaceObject obj in selection) obj.Thing.Selected = true; + } + + // Render selection + public override void RenderThingsSelection(IRenderer2D renderer, FindReplaceObject[] selection) + { + foreach (FindReplaceObject o in selection) + { + renderer.RenderThing(o.Thing, General.Colors.Selection, 1.0f); + } + } + + // Edit objects + public override void EditObjects(FindReplaceObject[] selection) + { + List things = new List(selection.Length); + foreach (FindReplaceObject o in selection) things.Add(o.Thing); + General.Interface.ShowEditThings(things); + } + + #endregion + } +} diff --git a/Source/Plugins/BuilderModes/FindReplace/FindVertexNumber.cs b/Source/Plugins/BuilderModes/FindReplace/FindVertexNumber.cs new file mode 100644 index 0000000..ff5d102 --- /dev/null +++ b/Source/Plugins/BuilderModes/FindReplace/FindVertexNumber.cs @@ -0,0 +1,132 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using CodeImp.DoomBuilder.Windows; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Geometry; +using System.Drawing; +using CodeImp.DoomBuilder.Editing; + +#endregion + +namespace CodeImp.DoomBuilder.BuilderModes +{ + [FindReplace("Vertex Index", BrowseButton = false, Replacable = false)] + internal class FindVertexNumber : FindReplaceType + { + #region ================== Constants + + #endregion + + #region ================== Variables + + #endregion + + #region ================== Properties + + #endregion + + #region ================== Constructor / Destructor + + // Constructor + public FindVertexNumber() + { + // Initialize + + } + + // Destructor + ~FindVertexNumber() + { + } + + #endregion + + #region ================== Methods + + // This is called when the browse button is pressed + public override string Browse(string initialvalue) + { + return ""; + } + + + // This is called to perform a search (and replace) + // Returns a list of items to show in the results list + // replacewith is null when not replacing + public override FindReplaceObject[] Find(string value, bool withinselection, string replacewith, bool keepselection) + { + List objs = new List(); + + // Interpret the number given + int index = 0; + if (int.TryParse(value, out index)) + { + Vertex v = General.Map.Map.GetVertexByIndex(index); + if (v != null) objs.Add(new FindReplaceObject(v, "Vertex " + index)); + } + + return objs.ToArray(); + } + + // This is called when a specific object is selected from the list + public override void ObjectSelected(FindReplaceObject[] selection) + { + if (selection.Length == 1) + { + ZoomToSelection(selection); + General.Interface.ShowVertexInfo(selection[0].Vertex); + } + else + General.Interface.HideInfo(); + + General.Map.Map.ClearAllSelected(); + foreach (FindReplaceObject obj in selection) obj.Vertex.Selected = true; + } + + // Render selection + public override void PlotSelection(IRenderer2D renderer, FindReplaceObject[] selection) + { + foreach (FindReplaceObject o in selection) + { + renderer.PlotVertex(o.Vertex, ColorCollection.SELECTION); + } + } + + // Edit objects + public override void EditObjects(FindReplaceObject[] selection) + { + List vertices = new List(selection.Length); + foreach (FindReplaceObject o in selection) vertices.Add(o.Vertex); + General.Interface.ShowEditVertices(vertices); + General.Map.Map.Update(); + } + + #endregion + } +} diff --git a/Source/Plugins/BuilderModes/General/Association.cs b/Source/Plugins/BuilderModes/General/Association.cs new file mode 100644 index 0000000..3f1e516 --- /dev/null +++ b/Source/Plugins/BuilderModes/General/Association.cs @@ -0,0 +1,87 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using CodeImp.DoomBuilder.Windows; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Geometry; +using System.Drawing; +using CodeImp.DoomBuilder.Editing; +using CodeImp.DoomBuilder.Plugins; +using CodeImp.DoomBuilder.Types; +using CodeImp.DoomBuilder.Config; + +#endregion + +namespace CodeImp.DoomBuilder.BuilderModes +{ + public struct Association + { + public int tag; + public UniversalType type; + + // This sets up the association + public Association(int tag, int type) + { + this.tag = tag; + this.type = (UniversalType)type; + } + + // This sets up the association + public Association(int tag, UniversalType type) + { + this.tag = tag; + this.type = type; + } + + // This sets up the association + public void Set(int tag, int type) + { + this.tag = tag; + this.type = (UniversalType)type; + } + + // This sets up the association + public void Set(int tag, UniversalType type) + { + this.tag = tag; + this.type = type; + } + + // This compares an association + public static bool operator ==(Association a, Association b) + { + return (a.tag == b.tag) && (a.type == b.type); + } + + // This compares an association + public static bool operator !=(Association a, Association b) + { + return (a.tag != b.tag) || (a.type != b.type); + } + } +} diff --git a/Source/Plugins/BuilderModes/General/BuilderPlug.cs b/Source/Plugins/BuilderModes/General/BuilderPlug.cs new file mode 100644 index 0000000..cc9ae85 --- /dev/null +++ b/Source/Plugins/BuilderModes/General/BuilderPlug.cs @@ -0,0 +1,481 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Runtime.InteropServices; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using CodeImp.DoomBuilder.Windows; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Geometry; +using System.Drawing; +using CodeImp.DoomBuilder.Editing; +using CodeImp.DoomBuilder.Plugins; +using CodeImp.DoomBuilder.Types; +using CodeImp.DoomBuilder.Config; +using CodeImp.DoomBuilder.Data; +using CodeImp.DoomBuilder.Controls; + +#endregion + +namespace CodeImp.DoomBuilder.BuilderModes +{ + public class BuilderPlug : Plug + { + #region ================== API Declarations + + [DllImport("user32.dll")] + internal static extern int GetWindowLong(IntPtr hWnd, int nIndex); + + #endregion + + #region ================== Constants + + internal const int WS_HSCROLL = 0x100000; + internal const int WS_VSCROLL = 0x200000; + internal const int GWL_STYLE = -16; + + #endregion + + #region ================== Variables + + // Static instance + private static BuilderPlug me; + + // Main objects + private MenusForm menusform; + private CurveLinedefsForm curvelinedefsform; + private FindReplaceForm findreplaceform; + private ErrorCheckForm errorcheckform; + private PreferencesForm preferencesform; + + // Dockers + private UndoRedoPanel undoredopanel; + private Docker undoredodocker; + + // Settings + private int showvisualthings; // 0 = none, 1 = sprite only, 2 = sprite caged + private bool usegravity; + private int changeheightbysidedef; // 0 = nothing, 1 = change ceiling, 2 = change floor + private bool editnewthing; + private bool editnewsector; + private bool additiveselect; + private bool autoclearselection; + private bool visualmodeclearselection; + private string copiedtexture; + private string copiedflat; + private Point copiedoffsets; + private VertexProperties copiedvertexprops; + private SectorProperties copiedsectorprops; + private SidedefProperties copiedsidedefprops; + private LinedefProperties copiedlinedefprops; + private ThingProperties copiedthingprops; + private bool viewselectionnumbers; + private float stitchrange; + private float highlightrange; + private float highlightthingsrange; + private float splitlinedefsrange; + private bool usehighlight; + private bool autodragonpaste; + + // villsa + private Lights[] sectorlights; + + #endregion + + #region ================== Properties + + public override string Name { get { return "Doom Builder"; } } + public static BuilderPlug Me { get { return me; } } + + // It is only safe to do this dynamically because we compile and distribute both + // the core and this plugin together with the same revision number! In third party + // plugins this should just contain a fixed number. + public override int MinimumRevision { get { return Assembly.GetExecutingAssembly().GetName().Version.Revision; } } + + public MenusForm MenusForm { get { return menusform; } } + public CurveLinedefsForm CurveLinedefsForm { get { return curvelinedefsform; } } + public FindReplaceForm FindReplaceForm { get { return findreplaceform; } } + public ErrorCheckForm ErrorCheckForm { get { return errorcheckform; } } + public PreferencesForm PreferencesForm { get { return preferencesform; } } + + // Settings + public int ShowVisualThings { get { return showvisualthings; } set { showvisualthings = value; } } + public bool UseGravity { get { return usegravity; } set { usegravity = value; } } + public int ChangeHeightBySidedef { get { return changeheightbysidedef; } } + public bool EditNewThing { get { return editnewthing; } } + public bool EditNewSector { get { return editnewsector; } } + public bool AdditiveSelect { get { return additiveselect; } } + public bool AutoClearSelection { get { return autoclearselection; } } + public bool VisualModeClearSelection { get { return visualmodeclearselection; } } + public string CopiedTexture { get { return copiedtexture; } set { copiedtexture = value; } } + public string CopiedFlat { get { return copiedflat; } set { copiedflat = value; } } + public Point CopiedOffsets { get { return copiedoffsets; } set { copiedoffsets = value; } } + public VertexProperties CopiedVertexProps { get { return copiedvertexprops; } set { copiedvertexprops = value; } } + public SectorProperties CopiedSectorProps { get { return copiedsectorprops; } set { copiedsectorprops = value; } } + public SidedefProperties CopiedSidedefProps { get { return copiedsidedefprops; } set { copiedsidedefprops = value; } } + public LinedefProperties CopiedLinedefProps { get { return copiedlinedefprops; } set { copiedlinedefprops = value; } } + public ThingProperties CopiedThingProps { get { return copiedthingprops; } set { copiedthingprops = value; } } + public bool ViewSelectionNumbers { get { return viewselectionnumbers; } set { viewselectionnumbers = value; } } + public float StitchRange { get { return stitchrange; } } + public float HighlightRange { get { return highlightrange; } } + public float HighlightThingsRange { get { return highlightthingsrange; } } + public float SplitLinedefsRange { get { return splitlinedefsrange; } } + public bool UseHighlight { get { return usehighlight; } set { usehighlight = value; } } + public bool AutoDragOnPaste { get { return autodragonpaste; } set { autodragonpaste = value; } } + //villsa + public Lights[] CopiedLights { get { return sectorlights; } set { sectorlights = value; } } + + #endregion + + #region ================== Initialize / Dispose + + // When plugin is initialized + public override void OnInitialize() + { + // Setup + me = this; + + // Settings + showvisualthings = 2; + usegravity = false; + usehighlight = true; + LoadSettings(); + + // Load menus form and register it + menusform = new MenusForm(); + menusform.Register(); + + // Load curve linedefs form + curvelinedefsform = new CurveLinedefsForm(); + + // Load find/replace form + findreplaceform = new FindReplaceForm(); + + // Load error checking form + errorcheckform = new ErrorCheckForm(); + + // villsa + sectorlights = new Lights[5]; + + // Load Undo\Redo docker + undoredopanel = new UndoRedoPanel(); + undoredodocker = new Docker("undoredo", "Undo / Redo", undoredopanel); + General.Interface.AddDocker(undoredodocker); + } + + // Disposer + public override void Dispose() + { + // Not already disposed? + if (!IsDisposed) + { + // Clean up + General.Interface.RemoveDocker(undoredodocker); + undoredopanel.Dispose(); + menusform.Unregister(); + menusform.Dispose(); + menusform = null; + curvelinedefsform.Dispose(); + curvelinedefsform = null; + findreplaceform.Dispose(); + findreplaceform = null; + errorcheckform.Dispose(); + errorcheckform = null; + + // Done + me = null; + base.Dispose(); + } + } + + #endregion + + #region ================== Methods + + // This loads the plugin settings + private void LoadSettings() + { + changeheightbysidedef = General.Settings.ReadPluginSetting("changeheightbysidedef", 0); + editnewthing = General.Settings.ReadPluginSetting("editnewthing", true); + editnewsector = General.Settings.ReadPluginSetting("editnewsector", false); + additiveselect = General.Settings.ReadPluginSetting("additiveselect", false); + autoclearselection = General.Settings.ReadPluginSetting("autoclearselection", false); + visualmodeclearselection = General.Settings.ReadPluginSetting("visualmodeclearselection", false); + viewselectionnumbers = General.Settings.ReadPluginSetting("viewselectionnumbers", true); + stitchrange = (float)General.Settings.ReadPluginSetting("stitchrange", 20); + highlightrange = (float)General.Settings.ReadPluginSetting("highlightrange", 20); + highlightthingsrange = (float)General.Settings.ReadPluginSetting("highlightthingsrange", 10); + splitlinedefsrange = (float)General.Settings.ReadPluginSetting("splitlinedefsrange", 10); + autodragonpaste = General.Settings.ReadPluginSetting("autodragonpaste", false); + } + + #endregion + + #region ================== Events + + // When floor surface geometry is created for classic modes + public override void OnSectorFloorSurfaceUpdate(Sector s, ref FlatVertex[] vertices) + { + ImageData img = General.Map.Data.GetFlatImage(s.LongFloorTexture); + if ((img != null) && img.IsImageLoaded) + { + // Make scalars + float sw = 1.0f / img.ScaledWidth; + float sh = 1.0f / img.ScaledHeight; + + // Make proper texture coordinates + for (int i = 0; i < vertices.Length; i++) + { + vertices[i].u = vertices[i].u * sw; + vertices[i].v = -vertices[i].v * sh; + } + } + } + + // When ceiling surface geometry is created for classic modes + public override void OnSectorCeilingSurfaceUpdate(Sector s, ref FlatVertex[] vertices) + { + ImageData img = General.Map.Data.GetFlatImage(s.LongCeilTexture); + if ((img != null) && img.IsImageLoaded) + { + // Make scalars + float sw = 1.0f / img.ScaledWidth; + float sh = 1.0f / img.ScaledHeight; + + // Make proper texture coordinates + for (int i = 0; i < vertices.Length; i++) + { + vertices[i].u = vertices[i].u * sw; + vertices[i].v = -vertices[i].v * sh; + } + } + } + + // When the editing mode changes + public override bool OnModeChange(EditMode oldmode, EditMode newmode) + { + // Show the correct menu for the new mode + menusform.ShowEditingModeMenu(newmode); + + return base.OnModeChange(oldmode, newmode); + } + + // When the Preferences dialog is shown + public override void OnShowPreferences(PreferencesController controller) + { + base.OnShowPreferences(controller); + + // Load preferences + preferencesform = new PreferencesForm(); + preferencesform.Setup(controller); + } + + // When the Preferences dialog is closed + public override void OnClosePreferences(PreferencesController controller) + { + base.OnClosePreferences(controller); + + // Apply settings that could have been changed + LoadSettings(); + + // Unload preferences + preferencesform.Dispose(); + preferencesform = null; + } + + // New map created + public override void OnMapNewEnd() + { + base.OnMapNewEnd(); + undoredopanel.SetBeginDescription("New Map"); + undoredopanel.UpdateList(); + } + + // Map opened + public override void OnMapOpenEnd() + { + base.OnMapOpenEnd(); + undoredopanel.SetBeginDescription("Opened Map"); + undoredopanel.UpdateList(); + } + + // Map closed + public override void OnMapCloseEnd() + { + base.OnMapCloseEnd(); + undoredopanel.UpdateList(); + } + + // Redo performed + public override void OnRedoEnd() + { + base.OnRedoEnd(); + undoredopanel.UpdateList(); + } + + // Undo performed + public override void OnUndoEnd() + { + base.OnUndoEnd(); + undoredopanel.UpdateList(); + } + + // Undo created + public override void OnUndoCreated() + { + base.OnUndoCreated(); + undoredopanel.UpdateList(); + } + + // Undo withdrawn + public override void OnUndoWithdrawn() + { + base.OnUndoWithdrawn(); + undoredopanel.UpdateList(); + } + + #endregion + + #region ================== Tools + + // This finds all class types that inherits from the given type + public Type[] FindClasses(Type t) + { + List found = new List(); + Type[] types; + + // Get all exported types + types = Assembly.GetExecutingAssembly().GetTypes(); + foreach (Type it in types) + { + // Compare types + if (t.IsAssignableFrom(it)) found.Add(it); + } + + // Return list + return found.ToArray(); + } + + // This renders the associated sectors/linedefs with the indication color + public void PlotAssociations(IRenderer2D renderer, Association asso) + { + // Tag must be above zero + if (asso.tag <= 0) return; + + // Sectors? + if (asso.type == UniversalType.SectorTag) + { + foreach (Sector s in General.Map.Map.Sectors) + if (s.Tag == asso.tag) renderer.PlotSector(s, General.Colors.Indication); + } + // Linedefs? + else if (asso.type == UniversalType.LinedefTag) + { + foreach (Linedef l in General.Map.Map.Linedefs) + if (l.Tag == asso.tag) renderer.PlotLinedef(l, General.Colors.Indication); + } + } + + + // This renders the associated things with the indication color + public void RenderAssociations(IRenderer2D renderer, Association asso) + { + // Tag must be above zero + if (asso.tag <= 0) return; + + // Things? + if (asso.type == UniversalType.ThingTag) + { + foreach (Thing t in General.Map.Map.Things) + if (t.Tag == asso.tag) renderer.RenderThing(t, General.Colors.Indication, 1.0f); + } + } + + + // This renders the associated sectors/linedefs with the indication color + public void PlotReverseAssociations(IRenderer2D renderer, Association asso) + { + // Tag must be above zero + if (asso.tag <= 0) return; + + // Doom style referencing to sectors? + if (General.Map.Config.LineTagIndicatesSectors && (asso.type == UniversalType.SectorTag)) + { + // Linedefs + foreach (Linedef l in General.Map.Map.Linedefs) + { + // Any action on this line? + if (l.Action > 0) + { + if (l.Tag == asso.tag) renderer.PlotLinedef(l, General.Colors.Indication); + } + } + } + else + { + // Linedefs + foreach (Linedef l in General.Map.Map.Linedefs) + { + // Known action on this line? + if ((l.Action > 0) && General.Map.Config.LinedefActions.ContainsKey(l.Action)) + { + LinedefActionInfo action = General.Map.Config.LinedefActions[l.Action]; + if ((action.Args[0].Type == (int)asso.type) && (l.Args[0] == asso.tag)) renderer.PlotLinedef(l, General.Colors.Indication); + if ((action.Args[1].Type == (int)asso.type) && (l.Args[1] == asso.tag)) renderer.PlotLinedef(l, General.Colors.Indication); + if ((action.Args[2].Type == (int)asso.type) && (l.Args[2] == asso.tag)) renderer.PlotLinedef(l, General.Colors.Indication); + if ((action.Args[3].Type == (int)asso.type) && (l.Args[3] == asso.tag)) renderer.PlotLinedef(l, General.Colors.Indication); + if ((action.Args[4].Type == (int)asso.type) && (l.Args[4] == asso.tag)) renderer.PlotLinedef(l, General.Colors.Indication); + } + } + } + } + + + // This renders the associated things with the indication color + public void RenderReverseAssociations(IRenderer2D renderer, Association asso) + { + // Tag must be above zero + if (asso.tag <= 0) return; + + // Things + foreach (Thing t in General.Map.Map.Things) + { + // Known action on this thing? + if ((t.Action > 0) && General.Map.Config.LinedefActions.ContainsKey(t.Action)) + { + LinedefActionInfo action = General.Map.Config.LinedefActions[t.Action]; + if ((action.Args[0].Type == (int)asso.type) && (t.Args[0] == asso.tag)) renderer.RenderThing(t, General.Colors.Indication, 1.0f); + if ((action.Args[1].Type == (int)asso.type) && (t.Args[1] == asso.tag)) renderer.RenderThing(t, General.Colors.Indication, 1.0f); + if ((action.Args[2].Type == (int)asso.type) && (t.Args[2] == asso.tag)) renderer.RenderThing(t, General.Colors.Indication, 1.0f); + if ((action.Args[3].Type == (int)asso.type) && (t.Args[3] == asso.tag)) renderer.RenderThing(t, General.Colors.Indication, 1.0f); + if ((action.Args[4].Type == (int)asso.type) && (t.Args[4] == asso.tag)) renderer.RenderThing(t, General.Colors.Indication, 1.0f); + } + } + } + + #endregion + } +} diff --git a/Source/Plugins/BuilderModes/General/CopyStructures.cs b/Source/Plugins/BuilderModes/General/CopyStructures.cs new file mode 100644 index 0000000..43828ce --- /dev/null +++ b/Source/Plugins/BuilderModes/General/CopyStructures.cs @@ -0,0 +1,244 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using CodeImp.DoomBuilder.Windows; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Geometry; +using System.Drawing; +using CodeImp.DoomBuilder.Editing; +using CodeImp.DoomBuilder.Plugins; +using CodeImp.DoomBuilder.Types; +using CodeImp.DoomBuilder.Config; +using CodeImp.DoomBuilder.Data; + +#endregion + +namespace CodeImp.DoomBuilder.BuilderModes +{ + // Vertex + public class VertexProperties + { + private UniFields fields; + + public VertexProperties(Vertex v) + { + fields = new UniFields(v.Fields); + } + + public void Apply(Vertex v) + { + v.Fields.BeforeFieldsChange(); + v.Fields.Clear(); + foreach (KeyValuePair uv in fields) + v.Fields.Add(uv.Key, new UniValue(uv.Value)); + } + } + + // Sector + public class SectorProperties + { + private int floorheight; + private int ceilheight; + private string floortexture; + private string ceilingtexture; + private int effect; + private int brightness; + private int tag; + private UniFields fields; + private Lights ceilColor; // villsa + private Lights floorColor; // villsa + private Lights thingColor; // villsa + private Lights upperColor; // villsa + private Lights lowerColor; // villsa + + public SectorProperties(Sector s) + { + floorheight = s.FloorHeight; + ceilheight = s.CeilHeight; + floortexture = s.FloorTexture; + ceilingtexture = s.CeilTexture; + brightness = s.Brightness; + effect = s.Effect; + tag = s.Tag; + fields = new UniFields(s.Fields); + ceilColor = s.CeilColor; // villsa + floorColor = s.FloorColor; // villsa + thingColor = s.ThingColor; // villsa + upperColor = s.TopColor; // villsa + lowerColor = s.LowerColor; // villsa + } + + public void Apply(Sector s) + { + s.FloorHeight = floorheight; + s.CeilHeight = ceilheight; + s.SetFloorTexture(floortexture); + s.SetCeilTexture(ceilingtexture); + s.Brightness = brightness; + s.Tag = tag; + s.CeilColor = ceilColor; // villsa + s.FloorColor = floorColor; // villsa + s.ThingColor = thingColor; // villsa + s.TopColor = upperColor; // villsa + s.LowerColor = lowerColor; // villsa + s.Fields.BeforeFieldsChange(); + s.Fields.Clear(); + foreach (KeyValuePair v in fields) + s.Fields.Add(v.Key, new UniValue(v.Value)); + + } + } + + // Sidedef + public class SidedefProperties + { + private string hightexture; + private string middletexture; + private string lowtexture; + private int offsetx; + private int offsety; + private UniFields fields; + + public SidedefProperties(Sidedef s) + { + hightexture = s.HighTexture; + middletexture = s.MiddleTexture; + lowtexture = s.LowTexture; + offsetx = s.OffsetX; + offsety = s.OffsetY; + fields = new UniFields(s.Fields); + } + + public void Apply(Sidedef s) + { + s.SetTextureHigh(hightexture); + s.SetTextureMid(middletexture); + s.SetTextureLow(lowtexture); + s.OffsetX = offsetx; + s.OffsetY = offsety; + s.Fields.BeforeFieldsChange(); + s.Fields.Clear(); + foreach (KeyValuePair v in fields) + s.Fields.Add(v.Key, new UniValue(v.Value)); + } + } + + // Linedef + public class LinedefProperties + { + private SidedefProperties front; + private SidedefProperties back; + private Dictionary flags; + private int action; + private int activate; + private int switchmask; // villsa 9/12/11 + private int tag; + private int[] args; + private UniFields fields; + + public LinedefProperties(Linedef l) + { + if (l.Front != null) + front = new SidedefProperties(l.Front); + else + front = null; + + if (l.Back != null) + back = new SidedefProperties(l.Back); + else + back = null; + + flags = l.GetFlags(); + action = l.Action; + switchmask = l.SwitchMask; // villsa 9/12/11 + activate = l.Activate; + tag = l.Tag; + args = (int[])(l.Args.Clone()); + fields = new UniFields(l.Fields); + } + + public void Apply(Linedef l) + { + if ((front != null) && (l.Front != null)) front.Apply(l.Front); + if ((back != null) && (l.Back != null)) back.Apply(l.Back); + l.ClearFlags(); + foreach (KeyValuePair f in flags) + l.SetFlag(f.Key, f.Value); + l.Action = action; + l.Activate = activate; + l.SwitchMask = switchmask; // villsa 9/12/11 + l.Tag = tag; + for (int i = 0; i < l.Args.Length; i++) + l.Args[i] = args[i]; + l.Fields.BeforeFieldsChange(); + l.Fields.Clear(); + foreach (KeyValuePair v in fields) + l.Fields.Add(v.Key, new UniValue(v.Value)); + } + } + + // Thing + public class ThingProperties + { + private int type; + private float angle; + private Dictionary flags; + private int tag; + private int action; + private int[] args; + private UniFields fields; + + public ThingProperties(Thing t) + { + type = t.Type; + angle = t.Angle; + flags = t.GetFlags(); + tag = t.Tag; + action = t.Action; + args = (int[])(t.Args.Clone()); + fields = new UniFields(t.Fields); + } + + public void Apply(Thing t) + { + t.Type = type; + t.Rotate(angle); + t.ClearFlags(); + foreach (KeyValuePair f in flags) + t.SetFlag(f.Key, f.Value); + t.Tag = tag; + t.Action = action; + for (int i = 0; i < t.Args.Length; i++) + t.Args[i] = args[i]; + t.Fields.BeforeFieldsChange(); + t.Fields.Clear(); + foreach (KeyValuePair v in fields) + t.Fields.Add(v.Key, new UniValue(v.Value)); + } + } +} diff --git a/Source/Plugins/BuilderModes/General/LineLengthLabel.cs b/Source/Plugins/BuilderModes/General/LineLengthLabel.cs new file mode 100644 index 0000000..56d86c6 --- /dev/null +++ b/Source/Plugins/BuilderModes/General/LineLengthLabel.cs @@ -0,0 +1,123 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using CodeImp.DoomBuilder.Windows; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Geometry; +using System.Drawing; +using CodeImp.DoomBuilder.Editing; + +#endregion + +namespace CodeImp.DoomBuilder.BuilderModes +{ + public class LineLengthLabel : IDisposable + { + #region ================== Constants + + private const int TEXT_CAPACITY = 10; + private const float TEXT_SCALE = 14f; + private const string VALUE_FORMAT = "0"; + + #endregion + + #region ================== Variables + + private TextLabel label; + private Vector2D start; + private Vector2D end; + + #endregion + + #region ================== Properties + + public TextLabel TextLabel { get { return label; } } + public Vector2D Start { get { return start; } set { start = value; Update(); } } + public Vector2D End { get { return end; } set { end = value; Update(); } } + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + public LineLengthLabel() + { + // Initialize + Initialize(); + } + + // Constructor + public LineLengthLabel(Vector2D start, Vector2D end) + { + // Initialize + Initialize(); + Move(start, end); + } + + // Initialization + private void Initialize() + { + label = new TextLabel(TEXT_CAPACITY); + label.AlignX = TextAlignmentX.Center; + label.AlignY = TextAlignmentY.Middle; + label.Color = General.Colors.Highlight; + label.Backcolor = General.Colors.Background; + label.Scale = TEXT_SCALE; + label.TransformCoords = true; + } + + // Disposer + public void Dispose() + { + label.Dispose(); + } + + #endregion + + #region ================== Methods + + // This updates the text + private void Update() + { + Vector2D delta = end - start; + float length = delta.GetLength(); + label.Text = length.ToString(VALUE_FORMAT); + label.Rectangle = new RectangleF(start.x + delta.x * 0.5f, start.y + delta.y * 0.5f, 0f, 0f); + } + + // This moves the label + public void Move(Vector2D start, Vector2D end) + { + this.start = start; + this.end = end; + Update(); + } + + #endregion + } +} diff --git a/Source/Plugins/BuilderModes/General/UndoGroup.cs b/Source/Plugins/BuilderModes/General/UndoGroup.cs new file mode 100644 index 0000000..943e55b --- /dev/null +++ b/Source/Plugins/BuilderModes/General/UndoGroup.cs @@ -0,0 +1,42 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using CodeImp.DoomBuilder.Map; + +#endregion + +namespace CodeImp.DoomBuilder.BuilderModes +{ + public class UndoGroup + { + public const int None = 0; + public const int FloorHeightChange = 1; + public const int CeilingHeightChange = 2; + public const int SectorBrightnessChange = 3; + public const int TextureOffsetChange = 4; + public const int SectorHeightChange = 5; + } +} diff --git a/Source/Plugins/BuilderModes/Interface/CurveLinedefsForm.Designer.cs b/Source/Plugins/BuilderModes/Interface/CurveLinedefsForm.Designer.cs new file mode 100644 index 0000000..b080668 --- /dev/null +++ b/Source/Plugins/BuilderModes/Interface/CurveLinedefsForm.Designer.cs @@ -0,0 +1,253 @@ +using CodeImp.DoomBuilder.Windows; + +namespace CodeImp.DoomBuilder.BuilderModes +{ + partial class CurveLinedefsForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.Windows.Forms.Label label1; + this.distancelabel = new System.Windows.Forms.Label(); + this.label3 = new System.Windows.Forms.Label(); + this.vertices = new CodeImp.DoomBuilder.Controls.NumericTextbox(); + this.distance = new CodeImp.DoomBuilder.Controls.NumericTextbox(); + this.angle = new CodeImp.DoomBuilder.Controls.NumericTextbox(); + this.verticesbar = new System.Windows.Forms.VScrollBar(); + this.distancebar = new System.Windows.Forms.VScrollBar(); + this.anglebar = new System.Windows.Forms.VScrollBar(); + this.cancel = new System.Windows.Forms.Button(); + this.apply = new System.Windows.Forms.Button(); + this.circular = new System.Windows.Forms.CheckBox(); + this.backwards = new System.Windows.Forms.CheckBox(); + label1 = new System.Windows.Forms.Label(); + this.SuspendLayout(); + // + // label1 + // + label1.AutoSize = true; + label1.Location = new System.Drawing.Point(12, 15); + label1.Name = "label1"; + label1.Size = new System.Drawing.Size(51, 14); + label1.TabIndex = 0; + label1.Text = "Vertices:"; + // + // distancelabel + // + this.distancelabel.AutoSize = true; + this.distancelabel.Location = new System.Drawing.Point(11, 47); + this.distancelabel.Name = "distancelabel"; + this.distancelabel.Size = new System.Drawing.Size(52, 14); + this.distancelabel.TabIndex = 1; + this.distancelabel.Text = "Distance:"; + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Location = new System.Drawing.Point(25, 79); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(38, 14); + this.label3.TabIndex = 2; + this.label3.Text = "Angle:"; + // + // vertices + // + this.vertices.AllowDecimal = false; + this.vertices.AllowNegative = false; + this.vertices.AllowRelative = false; + this.vertices.ImeMode = System.Windows.Forms.ImeMode.Off; + this.vertices.Location = new System.Drawing.Point(78, 12); + this.vertices.Name = "vertices"; + this.vertices.Size = new System.Drawing.Size(45, 20); + this.vertices.TabIndex = 0; + this.vertices.Text = "8"; + this.vertices.TextChanged += new System.EventHandler(this.vertices_TextChanged); + this.vertices.Leave += new System.EventHandler(this.vertices_Leave); + // + // distance + // + this.distance.AllowDecimal = false; + this.distance.AllowNegative = false; + this.distance.AllowRelative = false; + this.distance.ImeMode = System.Windows.Forms.ImeMode.Off; + this.distance.Location = new System.Drawing.Point(78, 44); + this.distance.Name = "distance"; + this.distance.Size = new System.Drawing.Size(45, 20); + this.distance.TabIndex = 2; + this.distance.Text = "128"; + this.distance.TextChanged += new System.EventHandler(this.distance_TextChanged); + this.distance.Leave += new System.EventHandler(this.distance_Leave); + // + // angle + // + this.angle.AllowDecimal = false; + this.angle.AllowNegative = false; + this.angle.AllowRelative = false; + this.angle.ImeMode = System.Windows.Forms.ImeMode.Off; + this.angle.Location = new System.Drawing.Point(78, 76); + this.angle.Name = "angle"; + this.angle.Size = new System.Drawing.Size(45, 20); + this.angle.TabIndex = 4; + this.angle.Text = "180"; + this.angle.TextChanged += new System.EventHandler(this.angle_TextChanged); + this.angle.Leave += new System.EventHandler(this.angle_Leave); + // + // verticesbar + // + this.verticesbar.LargeChange = 1; + this.verticesbar.Location = new System.Drawing.Point(125, 10); + this.verticesbar.Maximum = -1; + this.verticesbar.Minimum = -200; + this.verticesbar.Name = "verticesbar"; + this.verticesbar.Size = new System.Drawing.Size(19, 24); + this.verticesbar.TabIndex = 1; + this.verticesbar.Value = -8; + this.verticesbar.ValueChanged += new System.EventHandler(this.verticesbar_ValueChanged); + // + // distancebar + // + this.distancebar.LargeChange = 8; + this.distancebar.Location = new System.Drawing.Point(125, 42); + this.distancebar.Maximum = 0; + this.distancebar.Minimum = -10000; + this.distancebar.Name = "distancebar"; + this.distancebar.Size = new System.Drawing.Size(19, 24); + this.distancebar.SmallChange = 8; + this.distancebar.TabIndex = 3; + this.distancebar.Value = -128; + this.distancebar.ValueChanged += new System.EventHandler(this.distancebar_ValueChanged); + // + // anglebar + // + this.anglebar.LargeChange = 5; + this.anglebar.Location = new System.Drawing.Point(125, 74); + this.anglebar.Maximum = 0; + this.anglebar.Minimum = -180; + this.anglebar.Name = "anglebar"; + this.anglebar.Size = new System.Drawing.Size(19, 24); + this.anglebar.SmallChange = 5; + this.anglebar.TabIndex = 5; + this.anglebar.Value = -180; + this.anglebar.ValueChanged += new System.EventHandler(this.anglebar_ValueChanged); + // + // cancel + // + this.cancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.cancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.cancel.Location = new System.Drawing.Point(84, 167); + this.cancel.Name = "cancel"; + this.cancel.Size = new System.Drawing.Size(70, 25); + this.cancel.TabIndex = 9; + this.cancel.Text = "Cancel"; + this.cancel.UseVisualStyleBackColor = true; + this.cancel.Click += new System.EventHandler(this.cancel_Click); + // + // apply + // + this.apply.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.apply.Location = new System.Drawing.Point(7, 167); + this.apply.Name = "apply"; + this.apply.Size = new System.Drawing.Size(70, 25); + this.apply.TabIndex = 8; + this.apply.Text = "OK"; + this.apply.UseVisualStyleBackColor = true; + this.apply.Click += new System.EventHandler(this.apply_Click); + // + // circular + // + this.circular.AutoSize = true; + this.circular.Location = new System.Drawing.Point(22, 108); + this.circular.Name = "circular"; + this.circular.Size = new System.Drawing.Size(122, 18); + this.circular.TabIndex = 6; + this.circular.Text = "Fixed circular curve"; + this.circular.UseVisualStyleBackColor = true; + this.circular.CheckedChanged += new System.EventHandler(this.circular_CheckedChanged); + // + // backwards + // + this.backwards.AutoSize = true; + this.backwards.Location = new System.Drawing.Point(22, 132); + this.backwards.Name = "backwards"; + this.backwards.Size = new System.Drawing.Size(113, 18); + this.backwards.TabIndex = 7; + this.backwards.Text = "Curve backwards"; + this.backwards.UseVisualStyleBackColor = true; + this.backwards.CheckedChanged += new System.EventHandler(this.backwards_CheckedChanged); + // + // CurveLinedefsForm + // + this.AcceptButton = this.apply; + this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; + this.CancelButton = this.cancel; + this.ClientSize = new System.Drawing.Size(160, 199); + this.Controls.Add(this.backwards); + this.Controls.Add(this.circular); + this.Controls.Add(this.cancel); + this.Controls.Add(this.apply); + this.Controls.Add(this.anglebar); + this.Controls.Add(this.distancebar); + this.Controls.Add(this.verticesbar); + this.Controls.Add(this.angle); + this.Controls.Add(this.distance); + this.Controls.Add(this.vertices); + this.Controls.Add(this.label3); + this.Controls.Add(this.distancelabel); + this.Controls.Add(label1); + this.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedToolWindow; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "CurveLinedefsForm"; + this.Opacity = 0; + this.ShowIcon = false; + this.ShowInTaskbar = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.Manual; + this.Text = "Curve Linedefs"; + this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.CurveLinedefsForm_FormClosing); + this.HelpRequested += new System.Windows.Forms.HelpEventHandler(this.CurveLinedefsForm_HelpRequested); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Label label3; + private CodeImp.DoomBuilder.Controls.NumericTextbox vertices; + private CodeImp.DoomBuilder.Controls.NumericTextbox distance; + private CodeImp.DoomBuilder.Controls.NumericTextbox angle; + private System.Windows.Forms.VScrollBar verticesbar; + private System.Windows.Forms.VScrollBar distancebar; + private System.Windows.Forms.VScrollBar anglebar; + private System.Windows.Forms.Button cancel; + private System.Windows.Forms.Button apply; + private System.Windows.Forms.CheckBox circular; + private System.Windows.Forms.Label distancelabel; + private System.Windows.Forms.CheckBox backwards; + } +} \ No newline at end of file diff --git a/Source/Plugins/BuilderModes/Interface/CurveLinedefsForm.cs b/Source/Plugins/BuilderModes/Interface/CurveLinedefsForm.cs new file mode 100644 index 0000000..a7142d9 --- /dev/null +++ b/Source/Plugins/BuilderModes/Interface/CurveLinedefsForm.cs @@ -0,0 +1,213 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using CodeImp.DoomBuilder.Windows; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Geometry; +using System.Drawing; +using CodeImp.DoomBuilder.Editing; + +#endregion + +namespace CodeImp.DoomBuilder.BuilderModes +{ + public partial class CurveLinedefsForm : DelayedForm + { + #region ================== Constants + + private int MIN_VERTICES = 1; + private int MAX_VERTICES = 200; + private int MIN_DISTANCE = 0; + private int MAX_DISTANCE = 10000; + private int MIN_ANGLE = 1; + private int MAX_ANGLE = 350; + + #endregion + + #region ================== Properties + + public int Vertices { get { return -verticesbar.Value; } } + public float Distance { get { return (float)-distancebar.Value; } } + public float Angle { get { return Angle2D.DegToRad((float)-anglebar.Value); } } + public bool FixedCurve { get { return circular.Checked; } } + public bool Backwards { get { return backwards.Checked; } } + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + public CurveLinedefsForm() + { + // Initialize + InitializeComponent(); + + // Set negative properties for stupid + // scrollbars that work the other way around + verticesbar.Maximum = -MIN_VERTICES; + verticesbar.Minimum = -MAX_VERTICES; + distancebar.Maximum = -MIN_DISTANCE; + distancebar.Minimum = -MAX_DISTANCE; + anglebar.Maximum = -MIN_ANGLE; + anglebar.Minimum = -MAX_ANGLE; + } + + #endregion + + #region ================== Interface + + // Window closing + private void CurveLinedefsForm_FormClosing(object sender, FormClosingEventArgs e) + { + // User closing the window? + if (e.CloseReason == CloseReason.UserClosing) + { + // Just cancel + General.Editing.CancelMode(); + e.Cancel = true; + } + } + + // This shows the window + public void Show(Form owner) + { + // First time showing? + //if((this.Location.X == 0) && (this.Location.Y == 0)) + { + // Position at left-top of owner + this.Location = new Point(owner.Location.X + 20, owner.Location.Y + 90); + } + + // Show window + base.Show(owner); + } + + // Vertices bar changed + private void verticesbar_ValueChanged(object sender, EventArgs e) + { + int v = -verticesbar.Value; + vertices.Text = v.ToString(); + General.Interface.RedrawDisplay(); + } + + // Vertices loses focus + private void vertices_Leave(object sender, EventArgs e) + { + int v = -verticesbar.Value; + vertices.Text = v.ToString(); + } + + // Vertices change + private void vertices_TextChanged(object sender, EventArgs e) + { + int result = -vertices.GetResult(-verticesbar.Value); + if ((result >= verticesbar.Minimum) && (result <= verticesbar.Maximum)) verticesbar.Value = result; + } + + // Distance bar changed + private void distancebar_ValueChanged(object sender, EventArgs e) + { + int v = -distancebar.Value; + distance.Text = v.ToString(); + General.Interface.RedrawDisplay(); + } + + // Distance loses focus + private void distance_Leave(object sender, EventArgs e) + { + int v = -distancebar.Value; + distance.Text = v.ToString(); + } + + // Distance changed + private void distance_TextChanged(object sender, EventArgs e) + { + int result = -distance.GetResult(-distancebar.Value); + if ((result >= distancebar.Minimum) && (result <= distancebar.Maximum)) distancebar.Value = result; + } + + // Angle bar changed + private void anglebar_ValueChanged(object sender, EventArgs e) + { + int v = -anglebar.Value; + angle.Text = v.ToString(); + General.Interface.RedrawDisplay(); + } + + // Angle loses focus + private void angle_Leave(object sender, EventArgs e) + { + int v = -anglebar.Value; + angle.Text = v.ToString(); + } + + // Angle changed + private void angle_TextChanged(object sender, EventArgs e) + { + int result = -angle.GetResult(-anglebar.Value); + if ((result >= anglebar.Minimum) && (result <= anglebar.Maximum)) anglebar.Value = result; + } + + // Circular curve switched + private void circular_CheckedChanged(object sender, EventArgs e) + { + // Enable/disable controls + distance.Enabled = !circular.Checked; + distancebar.Enabled = !circular.Checked; + distancelabel.Enabled = !circular.Checked; + General.Interface.RedrawDisplay(); + } + + // Curve backwards switched + private void backwards_CheckedChanged(object sender, EventArgs e) + { + General.Interface.RedrawDisplay(); + } + + // Cancel clicked + private void cancel_Click(object sender, EventArgs e) + { + // Cancel now + General.Editing.CancelMode(); + } + + // Apply clicked + private void apply_Click(object sender, EventArgs e) + { + // Apply now + General.Editing.AcceptMode(); + } + + private void CurveLinedefsForm_HelpRequested(object sender, HelpEventArgs hlpevent) + { + General.ShowHelp("e_curvelinedefs.html"); + } + + #endregion + } +} \ No newline at end of file diff --git a/Source/Plugins/BuilderModes/Interface/CurveLinedefsForm.resx b/Source/Plugins/BuilderModes/Interface/CurveLinedefsForm.resx new file mode 100644 index 0000000..95dd186 --- /dev/null +++ b/Source/Plugins/BuilderModes/Interface/CurveLinedefsForm.resx @@ -0,0 +1,165 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + True + + + False + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + \ No newline at end of file diff --git a/Source/Plugins/BuilderModes/Interface/EditSelectionPanel.Designer.cs b/Source/Plugins/BuilderModes/Interface/EditSelectionPanel.Designer.cs new file mode 100644 index 0000000..aed9506 --- /dev/null +++ b/Source/Plugins/BuilderModes/Interface/EditSelectionPanel.Designer.cs @@ -0,0 +1,533 @@ +namespace CodeImp.DoomBuilder.BuilderModes +{ + partial class EditSelectionPanel + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.groupBox1 = new System.Windows.Forms.GroupBox(); + this.orgposy = new System.Windows.Forms.Button(); + this.orgposx = new System.Windows.Forms.Button(); + this.label16 = new System.Windows.Forms.Label(); + this.label19 = new System.Windows.Forms.Label(); + this.label6 = new System.Windows.Forms.Label(); + this.label5 = new System.Windows.Forms.Label(); + this.relposy = new CodeImp.DoomBuilder.Controls.ButtonsNumericTextbox(); + this.relposx = new CodeImp.DoomBuilder.Controls.ButtonsNumericTextbox(); + this.absposy = new CodeImp.DoomBuilder.Controls.ButtonsNumericTextbox(); + this.absposx = new CodeImp.DoomBuilder.Controls.ButtonsNumericTextbox(); + this.label2 = new System.Windows.Forms.Label(); + this.label1 = new System.Windows.Forms.Label(); + this.groupBox2 = new System.Windows.Forms.GroupBox(); + this.orgsizey = new System.Windows.Forms.Button(); + this.orgsizex = new System.Windows.Forms.Button(); + this.label12 = new System.Windows.Forms.Label(); + this.label9 = new System.Windows.Forms.Label(); + this.label8 = new System.Windows.Forms.Label(); + this.label7 = new System.Windows.Forms.Label(); + this.relsizey = new CodeImp.DoomBuilder.Controls.ButtonsNumericTextbox(); + this.relsizex = new CodeImp.DoomBuilder.Controls.ButtonsNumericTextbox(); + this.label4 = new System.Windows.Forms.Label(); + this.abssizey = new CodeImp.DoomBuilder.Controls.ButtonsNumericTextbox(); + this.abssizex = new CodeImp.DoomBuilder.Controls.ButtonsNumericTextbox(); + this.label3 = new System.Windows.Forms.Label(); + this.groupBox3 = new System.Windows.Forms.GroupBox(); + this.label14 = new System.Windows.Forms.Label(); + this.flipv = new System.Windows.Forms.Button(); + this.fliph = new System.Windows.Forms.Button(); + this.label13 = new System.Windows.Forms.Label(); + this.label11 = new System.Windows.Forms.Label(); + this.absrot = new CodeImp.DoomBuilder.Controls.ButtonsNumericTextbox(); + this.groupBox1.SuspendLayout(); + this.groupBox2.SuspendLayout(); + this.groupBox3.SuspendLayout(); + this.SuspendLayout(); + // + // groupBox1 + // + this.groupBox1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.groupBox1.Controls.Add(this.orgposy); + this.groupBox1.Controls.Add(this.orgposx); + this.groupBox1.Controls.Add(this.label16); + this.groupBox1.Controls.Add(this.label19); + this.groupBox1.Controls.Add(this.label6); + this.groupBox1.Controls.Add(this.label5); + this.groupBox1.Controls.Add(this.relposy); + this.groupBox1.Controls.Add(this.relposx); + this.groupBox1.Controls.Add(this.absposy); + this.groupBox1.Controls.Add(this.absposx); + this.groupBox1.Controls.Add(this.label2); + this.groupBox1.Controls.Add(this.label1); + this.groupBox1.Location = new System.Drawing.Point(3, 3); + this.groupBox1.Name = "groupBox1"; + this.groupBox1.Size = new System.Drawing.Size(243, 219); + this.groupBox1.TabIndex = 0; + this.groupBox1.TabStop = false; + this.groupBox1.Text = " Position "; + // + // orgposy + // + this.orgposy.FlatStyle = System.Windows.Forms.FlatStyle.Popup; + this.orgposy.Location = new System.Drawing.Point(109, 54); + this.orgposy.Name = "orgposy"; + this.orgposy.Size = new System.Drawing.Size(79, 24); + this.orgposy.TabIndex = 29; + this.orgposy.Text = "-2000"; + this.orgposy.UseVisualStyleBackColor = true; + this.orgposy.Click += new System.EventHandler(this.orgposy_Click); + // + // orgposx + // + this.orgposx.FlatStyle = System.Windows.Forms.FlatStyle.Popup; + this.orgposx.Location = new System.Drawing.Point(21, 54); + this.orgposx.Name = "orgposx"; + this.orgposx.Size = new System.Drawing.Size(79, 24); + this.orgposx.TabIndex = 28; + this.orgposx.Text = "-2000"; + this.orgposx.UseVisualStyleBackColor = true; + this.orgposx.Click += new System.EventHandler(this.orgposx_Click); + // + // label16 + // + this.label16.AutoSize = true; + this.label16.Location = new System.Drawing.Point(197, 59); + this.label16.Name = "label16"; + this.label16.Size = new System.Drawing.Size(21, 14); + this.label16.TabIndex = 26; + this.label16.Text = "mp"; + // + // label19 + // + this.label19.AutoSize = true; + this.label19.Location = new System.Drawing.Point(18, 37); + this.label19.Name = "label19"; + this.label19.Size = new System.Drawing.Size(46, 14); + this.label19.TabIndex = 23; + this.label19.Text = "Original:"; + // + // label6 + // + this.label6.AutoSize = true; + this.label6.Location = new System.Drawing.Point(197, 174); + this.label6.Name = "label6"; + this.label6.Size = new System.Drawing.Size(21, 14); + this.label6.TabIndex = 13; + this.label6.Text = "mp"; + // + // label5 + // + this.label5.AutoSize = true; + this.label5.Location = new System.Drawing.Point(197, 118); + this.label5.Name = "label5"; + this.label5.Size = new System.Drawing.Size(21, 14); + this.label5.TabIndex = 12; + this.label5.Text = "mp"; + // + // relposy + // + this.relposy.AllowDecimal = true; + this.relposy.AllowNegative = true; + this.relposy.AllowRelative = true; + this.relposy.ButtonStep = 1; + this.relposy.Location = new System.Drawing.Point(109, 169); + this.relposy.Name = "relposy"; + this.relposy.Size = new System.Drawing.Size(82, 24); + this.relposy.StepValues = null; + this.relposy.TabIndex = 11; + this.relposy.WhenEnterPressed += new System.EventHandler(this.relposy_Validated); + this.relposy.Validated += new System.EventHandler(this.relposy_Validated); + this.relposy.WhenButtonsClicked += new System.EventHandler(this.relposy_Validated); + this.relposy.WhenTextChanged += new System.EventHandler(this.WhenTextChanged); + // + // relposx + // + this.relposx.AllowDecimal = true; + this.relposx.AllowNegative = true; + this.relposx.AllowRelative = true; + this.relposx.ButtonStep = 1; + this.relposx.Location = new System.Drawing.Point(21, 169); + this.relposx.Name = "relposx"; + this.relposx.Size = new System.Drawing.Size(82, 24); + this.relposx.StepValues = null; + this.relposx.TabIndex = 10; + this.relposx.WhenEnterPressed += new System.EventHandler(this.relposx_Validated); + this.relposx.Validated += new System.EventHandler(this.relposx_Validated); + this.relposx.WhenButtonsClicked += new System.EventHandler(this.relposx_Validated); + this.relposx.WhenTextChanged += new System.EventHandler(this.WhenTextChanged); + // + // absposy + // + this.absposy.AllowDecimal = true; + this.absposy.AllowNegative = true; + this.absposy.AllowRelative = true; + this.absposy.ButtonStep = 1; + this.absposy.Location = new System.Drawing.Point(109, 113); + this.absposy.Name = "absposy"; + this.absposy.Size = new System.Drawing.Size(82, 24); + this.absposy.StepValues = null; + this.absposy.TabIndex = 9; + this.absposy.WhenEnterPressed += new System.EventHandler(this.absposy_Validated); + this.absposy.Validated += new System.EventHandler(this.absposy_Validated); + this.absposy.WhenButtonsClicked += new System.EventHandler(this.absposy_Validated); + this.absposy.WhenTextChanged += new System.EventHandler(this.WhenTextChanged); + // + // absposx + // + this.absposx.AllowDecimal = true; + this.absposx.AllowNegative = true; + this.absposx.AllowRelative = true; + this.absposx.ButtonStep = 1; + this.absposx.Location = new System.Drawing.Point(21, 113); + this.absposx.Name = "absposx"; + this.absposx.Size = new System.Drawing.Size(82, 24); + this.absposx.StepValues = null; + this.absposx.TabIndex = 8; + this.absposx.WhenEnterPressed += new System.EventHandler(this.absposx_Validated); + this.absposx.Validated += new System.EventHandler(this.absposx_Validated); + this.absposx.WhenButtonsClicked += new System.EventHandler(this.absposx_Validated); + this.absposx.WhenTextChanged += new System.EventHandler(this.WhenTextChanged); + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(18, 152); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(48, 14); + this.label2.TabIndex = 1; + this.label2.Text = "Relative:"; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(18, 96); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(53, 14); + this.label1.TabIndex = 0; + this.label1.Text = "Absolute:"; + // + // groupBox2 + // + this.groupBox2.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.groupBox2.Controls.Add(this.orgsizey); + this.groupBox2.Controls.Add(this.orgsizex); + this.groupBox2.Controls.Add(this.label12); + this.groupBox2.Controls.Add(this.label9); + this.groupBox2.Controls.Add(this.label8); + this.groupBox2.Controls.Add(this.label7); + this.groupBox2.Controls.Add(this.relsizey); + this.groupBox2.Controls.Add(this.relsizex); + this.groupBox2.Controls.Add(this.label4); + this.groupBox2.Controls.Add(this.abssizey); + this.groupBox2.Controls.Add(this.abssizex); + this.groupBox2.Controls.Add(this.label3); + this.groupBox2.Location = new System.Drawing.Point(3, 228); + this.groupBox2.Name = "groupBox2"; + this.groupBox2.Size = new System.Drawing.Size(243, 219); + this.groupBox2.TabIndex = 1; + this.groupBox2.TabStop = false; + this.groupBox2.Text = " Size "; + // + // orgsizey + // + this.orgsizey.FlatStyle = System.Windows.Forms.FlatStyle.Popup; + this.orgsizey.Location = new System.Drawing.Point(109, 51); + this.orgsizey.Name = "orgsizey"; + this.orgsizey.Size = new System.Drawing.Size(79, 24); + this.orgsizey.TabIndex = 31; + this.orgsizey.Text = "-2000"; + this.orgsizey.UseVisualStyleBackColor = true; + this.orgsizey.Click += new System.EventHandler(this.orgsizey_Click); + // + // orgsizex + // + this.orgsizex.FlatStyle = System.Windows.Forms.FlatStyle.Popup; + this.orgsizex.Location = new System.Drawing.Point(21, 51); + this.orgsizex.Name = "orgsizex"; + this.orgsizex.Size = new System.Drawing.Size(79, 24); + this.orgsizex.TabIndex = 30; + this.orgsizex.Text = "-2000"; + this.orgsizex.UseVisualStyleBackColor = true; + this.orgsizex.Click += new System.EventHandler(this.orgsizex_Click); + // + // label12 + // + this.label12.AutoSize = true; + this.label12.Location = new System.Drawing.Point(197, 56); + this.label12.Name = "label12"; + this.label12.Size = new System.Drawing.Size(21, 14); + this.label12.TabIndex = 21; + this.label12.Text = "mp"; + // + // label9 + // + this.label9.AutoSize = true; + this.label9.Location = new System.Drawing.Point(18, 34); + this.label9.Name = "label9"; + this.label9.Size = new System.Drawing.Size(46, 14); + this.label9.TabIndex = 18; + this.label9.Text = "Original:"; + // + // label8 + // + this.label8.AutoSize = true; + this.label8.Location = new System.Drawing.Point(197, 173); + this.label8.Name = "label8"; + this.label8.Size = new System.Drawing.Size(17, 14); + this.label8.TabIndex = 17; + this.label8.Text = "%"; + // + // label7 + // + this.label7.AutoSize = true; + this.label7.Location = new System.Drawing.Point(197, 115); + this.label7.Name = "label7"; + this.label7.Size = new System.Drawing.Size(21, 14); + this.label7.TabIndex = 16; + this.label7.Text = "mp"; + // + // relsizey + // + this.relsizey.AllowDecimal = true; + this.relsizey.AllowNegative = true; + this.relsizey.AllowRelative = true; + this.relsizey.ButtonStep = 1; + this.relsizey.Location = new System.Drawing.Point(109, 168); + this.relsizey.Name = "relsizey"; + this.relsizey.Size = new System.Drawing.Size(82, 24); + this.relsizey.StepValues = null; + this.relsizey.TabIndex = 15; + this.relsizey.WhenEnterPressed += new System.EventHandler(this.relsizey_Validated); + this.relsizey.Validated += new System.EventHandler(this.relsizey_Validated); + this.relsizey.WhenButtonsClicked += new System.EventHandler(this.relsizey_Validated); + this.relsizey.WhenTextChanged += new System.EventHandler(this.WhenTextChanged); + // + // relsizex + // + this.relsizex.AllowDecimal = true; + this.relsizex.AllowNegative = true; + this.relsizex.AllowRelative = true; + this.relsizex.ButtonStep = 1; + this.relsizex.Location = new System.Drawing.Point(21, 168); + this.relsizex.Name = "relsizex"; + this.relsizex.Size = new System.Drawing.Size(82, 24); + this.relsizex.StepValues = null; + this.relsizex.TabIndex = 14; + this.relsizex.WhenEnterPressed += new System.EventHandler(this.relsizex_Validated); + this.relsizex.Validated += new System.EventHandler(this.relsizex_Validated); + this.relsizex.WhenButtonsClicked += new System.EventHandler(this.relsizex_Validated); + this.relsizex.WhenTextChanged += new System.EventHandler(this.WhenTextChanged); + // + // label4 + // + this.label4.AutoSize = true; + this.label4.Location = new System.Drawing.Point(18, 151); + this.label4.Name = "label4"; + this.label4.Size = new System.Drawing.Size(37, 14); + this.label4.TabIndex = 13; + this.label4.Text = "Scale:"; + // + // abssizey + // + this.abssizey.AllowDecimal = true; + this.abssizey.AllowNegative = true; + this.abssizey.AllowRelative = true; + this.abssizey.ButtonStep = 1; + this.abssizey.Location = new System.Drawing.Point(109, 110); + this.abssizey.Name = "abssizey"; + this.abssizey.Size = new System.Drawing.Size(82, 24); + this.abssizey.StepValues = null; + this.abssizey.TabIndex = 12; + this.abssizey.WhenEnterPressed += new System.EventHandler(this.abssizey_Validated); + this.abssizey.Validated += new System.EventHandler(this.abssizey_Validated); + this.abssizey.WhenButtonsClicked += new System.EventHandler(this.abssizey_Validated); + this.abssizey.WhenTextChanged += new System.EventHandler(this.WhenTextChanged); + // + // abssizex + // + this.abssizex.AllowDecimal = true; + this.abssizex.AllowNegative = true; + this.abssizex.AllowRelative = true; + this.abssizex.ButtonStep = 1; + this.abssizex.Location = new System.Drawing.Point(21, 110); + this.abssizex.Name = "abssizex"; + this.abssizex.Size = new System.Drawing.Size(82, 24); + this.abssizex.StepValues = null; + this.abssizex.TabIndex = 11; + this.abssizex.WhenEnterPressed += new System.EventHandler(this.abssizex_Validated); + this.abssizex.Validated += new System.EventHandler(this.abssizex_Validated); + this.abssizex.WhenButtonsClicked += new System.EventHandler(this.abssizex_Validated); + this.abssizex.WhenTextChanged += new System.EventHandler(this.WhenTextChanged); + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Location = new System.Drawing.Point(18, 93); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(53, 14); + this.label3.TabIndex = 10; + this.label3.Text = "Absolute:"; + // + // groupBox3 + // + this.groupBox3.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.groupBox3.Controls.Add(this.label14); + this.groupBox3.Controls.Add(this.flipv); + this.groupBox3.Controls.Add(this.fliph); + this.groupBox3.Controls.Add(this.label13); + this.groupBox3.Controls.Add(this.label11); + this.groupBox3.Controls.Add(this.absrot); + this.groupBox3.Location = new System.Drawing.Point(3, 453); + this.groupBox3.Name = "groupBox3"; + this.groupBox3.Size = new System.Drawing.Size(243, 129); + this.groupBox3.TabIndex = 2; + this.groupBox3.TabStop = false; + this.groupBox3.Text = " Transform "; + // + // label14 + // + this.label14.AutoSize = true; + this.label14.Location = new System.Drawing.Point(18, 81); + this.label14.Name = "label14"; + this.label14.Size = new System.Drawing.Size(46, 14); + this.label14.TabIndex = 27; + this.label14.Text = "Flipping:"; + // + // flipv + // + this.flipv.FlatStyle = System.Windows.Forms.FlatStyle.Popup; + this.flipv.Image = global::CodeImp.DoomBuilder.BuilderModes.Properties.Resources.FlipSelectionV; + this.flipv.Location = new System.Drawing.Point(123, 73); + this.flipv.Name = "flipv"; + this.flipv.Size = new System.Drawing.Size(30, 30); + this.flipv.TabIndex = 26; + this.flipv.UseVisualStyleBackColor = true; + this.flipv.Click += new System.EventHandler(this.flipv_Click); + // + // fliph + // + this.fliph.FlatStyle = System.Windows.Forms.FlatStyle.Popup; + this.fliph.Image = global::CodeImp.DoomBuilder.BuilderModes.Properties.Resources.FlipSelectionH; + this.fliph.Location = new System.Drawing.Point(87, 73); + this.fliph.Name = "fliph"; + this.fliph.Size = new System.Drawing.Size(30, 30); + this.fliph.TabIndex = 25; + this.fliph.UseVisualStyleBackColor = true; + this.fliph.Click += new System.EventHandler(this.fliph_Click); + // + // label13 + // + this.label13.AutoSize = true; + this.label13.Location = new System.Drawing.Point(18, 39); + this.label13.Name = "label13"; + this.label13.Size = new System.Drawing.Size(49, 14); + this.label13.TabIndex = 23; + this.label13.Text = "Rotation:"; + // + // label11 + // + this.label11.AutoSize = true; + this.label11.Location = new System.Drawing.Point(175, 39); + this.label11.Name = "label11"; + this.label11.Size = new System.Drawing.Size(12, 14); + this.label11.TabIndex = 22; + this.label11.Text = "�"; + // + // absrot + // + this.absrot.AllowDecimal = true; + this.absrot.AllowNegative = true; + this.absrot.AllowRelative = true; + this.absrot.ButtonStep = 1; + this.absrot.Location = new System.Drawing.Point(87, 34); + this.absrot.Name = "absrot"; + this.absrot.Size = new System.Drawing.Size(82, 24); + this.absrot.StepValues = null; + this.absrot.TabIndex = 24; + this.absrot.WhenEnterPressed += new System.EventHandler(this.absrot_Validated); + this.absrot.Validated += new System.EventHandler(this.absrot_Validated); + this.absrot.WhenButtonsClicked += new System.EventHandler(this.absrot_Validated); + this.absrot.WhenTextChanged += new System.EventHandler(this.WhenTextChanged); + // + // EditSelectionPanel + // + this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; + this.Controls.Add(this.groupBox3); + this.Controls.Add(this.groupBox2); + this.Controls.Add(this.groupBox1); + this.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.Name = "EditSelectionPanel"; + this.Size = new System.Drawing.Size(249, 652); + this.groupBox1.ResumeLayout(false); + this.groupBox1.PerformLayout(); + this.groupBox2.ResumeLayout(false); + this.groupBox2.PerformLayout(); + this.groupBox3.ResumeLayout(false); + this.groupBox3.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.GroupBox groupBox1; + private System.Windows.Forms.GroupBox groupBox2; + private System.Windows.Forms.GroupBox groupBox3; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.Label label1; + private CodeImp.DoomBuilder.Controls.ButtonsNumericTextbox relposy; + private CodeImp.DoomBuilder.Controls.ButtonsNumericTextbox relposx; + private CodeImp.DoomBuilder.Controls.ButtonsNumericTextbox absposy; + private CodeImp.DoomBuilder.Controls.ButtonsNumericTextbox absposx; + private System.Windows.Forms.Label label6; + private System.Windows.Forms.Label label5; + private System.Windows.Forms.Label label8; + private System.Windows.Forms.Label label7; + private CodeImp.DoomBuilder.Controls.ButtonsNumericTextbox relsizey; + private CodeImp.DoomBuilder.Controls.ButtonsNumericTextbox relsizex; + private System.Windows.Forms.Label label4; + private CodeImp.DoomBuilder.Controls.ButtonsNumericTextbox abssizey; + private CodeImp.DoomBuilder.Controls.ButtonsNumericTextbox abssizex; + private System.Windows.Forms.Label label3; + private System.Windows.Forms.Label label9; + private System.Windows.Forms.Label label12; + private CodeImp.DoomBuilder.Controls.ButtonsNumericTextbox absrot; + private System.Windows.Forms.Label label13; + private System.Windows.Forms.Label label11; + private System.Windows.Forms.Label label14; + private System.Windows.Forms.Button flipv; + private System.Windows.Forms.Button fliph; + private System.Windows.Forms.Label label16; + private System.Windows.Forms.Label label19; + private System.Windows.Forms.Button orgposx; + private System.Windows.Forms.Button orgposy; + private System.Windows.Forms.Button orgsizey; + private System.Windows.Forms.Button orgsizex; + } +} diff --git a/Source/Plugins/BuilderModes/Interface/EditSelectionPanel.cs b/Source/Plugins/BuilderModes/Interface/EditSelectionPanel.cs new file mode 100644 index 0000000..a5bc483 --- /dev/null +++ b/Source/Plugins/BuilderModes/Interface/EditSelectionPanel.cs @@ -0,0 +1,223 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Text; +using System.Windows.Forms; +using Microsoft.Win32; +using CodeImp.DoomBuilder.Actions; +using CodeImp.DoomBuilder.Data; +using CodeImp.DoomBuilder.Config; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Controls; +using CodeImp.DoomBuilder.Windows; +using System.Reflection; +using System.Globalization; +using System.Threading; +using CodeImp.DoomBuilder.Editing; +using CodeImp.DoomBuilder.Geometry; + +#endregion + +namespace CodeImp.DoomBuilder.BuilderModes +{ + public partial class EditSelectionPanel : UserControl + { + #region ================== Constants + + #endregion + + #region ================== Variables + + // Editing mode + EditSelectionMode mode; + + // Input + private bool userinput; + + // Values + Vector2D orgpos; + Vector2D orgsize; + Vector2D abspos; + Vector2D relpos; + Vector2D abssize; + Vector2D relsize; + float absrotate; + + #endregion + + #region ================== Constructor + + // Constructor + public EditSelectionPanel(EditSelectionMode mode) + { + InitializeComponent(); + this.mode = mode; + } + + #endregion + + #region ================== Methods + + // This sets the original size + public void ShowOriginalValues(Vector2D pos, Vector2D size) + { + // Set values + this.orgpos = pos; + this.orgsize = size; + + // Set controls + orgposx.Text = pos.x.ToString(); + orgposy.Text = pos.y.ToString(); + orgsizex.Text = size.x.ToString(); + orgsizey.Text = size.y.ToString(); + } + + // This sets the dynamic values + public void ShowCurrentValues(Vector2D pos, Vector2D relpos, Vector2D size, Vector2D relsize, float rotation) + { + // Set values + this.abspos = pos; + this.relpos = relpos; + this.abssize = size; + this.relsize = relsize; + this.absrotate = Angle2D.RadToDeg(rotation); + + // Set controls + absposx.Text = pos.x.ToString("0.#"); + absposy.Text = pos.y.ToString("0.#"); + relposx.Text = relpos.x.ToString("0.#"); + relposy.Text = relpos.y.ToString("0.#"); + abssizex.Text = size.x.ToString("0.#"); + abssizey.Text = size.y.ToString("0.#"); + relsizex.Text = relsize.x.ToString("0.#"); + relsizey.Text = relsize.y.ToString("0.#"); + absrot.Text = this.absrotate.ToString("0.#"); + + userinput = false; + } + + #endregion + + #region ================== Events + + // User input given + private void WhenTextChanged(object sender, EventArgs e) + { + userinput = true; + } + + private void absposx_Validated(object sender, EventArgs e) + { + if (userinput) + mode.SetAbsPosX(absposx.GetResultFloat(this.abspos.x)); + } + + private void absposy_Validated(object sender, EventArgs e) + { + if (userinput) + mode.SetAbsPosY(absposy.GetResultFloat(this.abspos.y)); + } + + private void relposx_Validated(object sender, EventArgs e) + { + if (userinput) + mode.SetRelPosX(relposx.GetResultFloat(this.relpos.x)); + } + + private void relposy_Validated(object sender, EventArgs e) + { + if (userinput) + mode.SetRelPosY(relposy.GetResultFloat(this.relpos.y)); + } + + private void abssizex_Validated(object sender, EventArgs e) + { + if (userinput) + mode.SetAbsSizeX(abssizex.GetResultFloat(this.abssize.x)); + } + + private void abssizey_Validated(object sender, EventArgs e) + { + if (userinput) + mode.SetAbsSizeY(abssizey.GetResultFloat(this.abssize.y)); + } + + private void relsizex_Validated(object sender, EventArgs e) + { + if (userinput) + mode.SetRelSizeX(relsizex.GetResultFloat(this.relsize.x)); + } + + private void relsizey_Validated(object sender, EventArgs e) + { + if (userinput) + mode.SetRelSizeY(relsizey.GetResultFloat(this.relsize.y)); + } + + private void absrot_Validated(object sender, EventArgs e) + { + if (userinput) + { + float rad = Angle2D.DegToRad(absrot.GetResultFloat(this.absrotate)); + mode.SetAbsRotation(rad); + } + } + + private void fliph_Click(object sender, EventArgs e) + { + General.Actions.InvokeAction("buildermodes_flipselectionh"); + General.Interface.FocusDisplay(); + } + + private void flipv_Click(object sender, EventArgs e) + { + General.Actions.InvokeAction("buildermodes_flipselectionv"); + General.Interface.FocusDisplay(); + } + + private void orgposx_Click(object sender, EventArgs e) + { + mode.SetAbsPosX(orgpos.x); + General.Interface.FocusDisplay(); + } + + private void orgposy_Click(object sender, EventArgs e) + { + mode.SetAbsPosY(orgpos.y); + General.Interface.FocusDisplay(); + } + + private void orgsizex_Click(object sender, EventArgs e) + { + mode.SetAbsSizeX(orgsize.x); + General.Interface.FocusDisplay(); + } + + private void orgsizey_Click(object sender, EventArgs e) + { + mode.SetAbsSizeY(orgsize.y); + General.Interface.FocusDisplay(); + } + + #endregion + } +} diff --git a/Source/Plugins/BuilderModes/Interface/EditSelectionPanel.resx b/Source/Plugins/BuilderModes/Interface/EditSelectionPanel.resx new file mode 100644 index 0000000..66178e0 --- /dev/null +++ b/Source/Plugins/BuilderModes/Interface/EditSelectionPanel.resx @@ -0,0 +1,222 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + \ No newline at end of file diff --git a/Source/Plugins/BuilderModes/Interface/ErrorCheckForm.Designer.cs b/Source/Plugins/BuilderModes/Interface/ErrorCheckForm.Designer.cs new file mode 100644 index 0000000..a8b0cd6 --- /dev/null +++ b/Source/Plugins/BuilderModes/Interface/ErrorCheckForm.Designer.cs @@ -0,0 +1,208 @@ +namespace CodeImp.DoomBuilder.BuilderModes +{ + partial class ErrorCheckForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.checks = new CodeImp.DoomBuilder.Controls.CheckboxArrayControl(); + this.buttoncheck = new System.Windows.Forms.Button(); + this.results = new System.Windows.Forms.ListBox(); + this.resultspanel = new System.Windows.Forms.Panel(); + this.fix3 = new System.Windows.Forms.Button(); + this.fix2 = new System.Windows.Forms.Button(); + this.resultinfo = new System.Windows.Forms.Label(); + this.fix1 = new System.Windows.Forms.Button(); + this.progress = new System.Windows.Forms.ProgressBar(); + this.closebutton = new System.Windows.Forms.Button(); + this.resultspanel.SuspendLayout(); + this.SuspendLayout(); + // + // checks + // + this.checks.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.checks.AutoScroll = true; + this.checks.AutoSize = true; + this.checks.Columns = 2; + this.checks.Location = new System.Drawing.Point(10, 15); + this.checks.Margin = new System.Windows.Forms.Padding(1); + this.checks.Name = "checks"; + this.checks.Size = new System.Drawing.Size(360, 63); + this.checks.TabIndex = 0; + // + // buttoncheck + // + this.buttoncheck.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.buttoncheck.Location = new System.Drawing.Point(254, 89); + this.buttoncheck.Margin = new System.Windows.Forms.Padding(1); + this.buttoncheck.Name = "buttoncheck"; + this.buttoncheck.Size = new System.Drawing.Size(116, 25); + this.buttoncheck.TabIndex = 1; + this.buttoncheck.Text = "Start Analysis"; + this.buttoncheck.UseVisualStyleBackColor = true; + this.buttoncheck.Click += new System.EventHandler(this.buttoncheck_Click); + // + // results + // + this.results.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.results.FormattingEnabled = true; + this.results.IntegralHeight = false; + this.results.ItemHeight = 14; + this.results.Location = new System.Drawing.Point(10, 34); + this.results.Margin = new System.Windows.Forms.Padding(1); + this.results.Name = "results"; + this.results.Size = new System.Drawing.Size(360, 175); + this.results.TabIndex = 0; + this.results.SelectedIndexChanged += new System.EventHandler(this.results_SelectedIndexChanged); + // + // resultspanel + // + this.resultspanel.Controls.Add(this.fix3); + this.resultspanel.Controls.Add(this.fix2); + this.resultspanel.Controls.Add(this.resultinfo); + this.resultspanel.Controls.Add(this.fix1); + this.resultspanel.Controls.Add(this.progress); + this.resultspanel.Controls.Add(this.results); + this.resultspanel.Location = new System.Drawing.Point(0, 124); + this.resultspanel.Name = "resultspanel"; + this.resultspanel.Size = new System.Drawing.Size(383, 329); + this.resultspanel.TabIndex = 2; + // + // fix3 + // + this.fix3.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.fix3.Location = new System.Drawing.Point(256, 292); + this.fix3.Name = "fix3"; + this.fix3.Size = new System.Drawing.Size(114, 26); + this.fix3.TabIndex = 3; + this.fix3.Text = "Fix 3"; + this.fix3.UseVisualStyleBackColor = true; + this.fix3.Visible = false; + this.fix3.Click += new System.EventHandler(this.fix3_Click); + // + // fix2 + // + this.fix2.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.fix2.Location = new System.Drawing.Point(133, 292); + this.fix2.Name = "fix2"; + this.fix2.Size = new System.Drawing.Size(114, 26); + this.fix2.TabIndex = 2; + this.fix2.Text = "Fix 2"; + this.fix2.UseVisualStyleBackColor = true; + this.fix2.Visible = false; + this.fix2.Click += new System.EventHandler(this.fix2_Click); + // + // resultinfo + // + this.resultinfo.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.resultinfo.Enabled = false; + this.resultinfo.Location = new System.Drawing.Point(12, 215); + this.resultinfo.Name = "resultinfo"; + this.resultinfo.Size = new System.Drawing.Size(358, 74); + this.resultinfo.TabIndex = 5; + this.resultinfo.Text = "Select a result from the list to see more information."; + // + // fix1 + // + this.fix1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.fix1.Location = new System.Drawing.Point(10, 292); + this.fix1.Name = "fix1"; + this.fix1.Size = new System.Drawing.Size(114, 26); + this.fix1.TabIndex = 1; + this.fix1.Text = "Fix 1"; + this.fix1.UseVisualStyleBackColor = true; + this.fix1.Visible = false; + this.fix1.Click += new System.EventHandler(this.fix1_Click); + // + // progress + // + this.progress.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.progress.Location = new System.Drawing.Point(10, 3); + this.progress.Margin = new System.Windows.Forms.Padding(1); + this.progress.Name = "progress"; + this.progress.Size = new System.Drawing.Size(360, 18); + this.progress.TabIndex = 3; + this.progress.Value = 30; + // + // closebutton + // + this.closebutton.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.closebutton.Location = new System.Drawing.Point(-500, 134); + this.closebutton.Name = "closebutton"; + this.closebutton.Size = new System.Drawing.Size(116, 25); + this.closebutton.TabIndex = 4; + this.closebutton.Text = "Close"; + this.closebutton.UseVisualStyleBackColor = true; + this.closebutton.Click += new System.EventHandler(this.closebutton_Click); + // + // ErrorCheckForm + // + this.AcceptButton = this.buttoncheck; + this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; + this.CancelButton = this.closebutton; + this.ClientSize = new System.Drawing.Size(380, 453); + this.Controls.Add(this.closebutton); + this.Controls.Add(this.resultspanel); + this.Controls.Add(this.buttoncheck); + this.Controls.Add(this.checks); + this.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedToolWindow; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "ErrorCheckForm"; + this.Opacity = 0; + this.ShowIcon = false; + this.ShowInTaskbar = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.Manual; + this.Text = "Map Analysis"; + this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.ErrorCheckForm_FormClosing); + this.HelpRequested += new System.Windows.Forms.HelpEventHandler(this.ErrorCheckForm_HelpRequested); + this.resultspanel.ResumeLayout(false); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private CodeImp.DoomBuilder.Controls.CheckboxArrayControl checks; + private System.Windows.Forms.Button buttoncheck; + private System.Windows.Forms.ListBox results; + private System.Windows.Forms.Panel resultspanel; + private System.Windows.Forms.ProgressBar progress; + private System.Windows.Forms.Button fix3; + private System.Windows.Forms.Button fix2; + private System.Windows.Forms.Label resultinfo; + private System.Windows.Forms.Button fix1; + private System.Windows.Forms.Button closebutton; + } +} \ No newline at end of file diff --git a/Source/Plugins/BuilderModes/Interface/ErrorCheckForm.cs b/Source/Plugins/BuilderModes/Interface/ErrorCheckForm.cs new file mode 100644 index 0000000..e13afd4 --- /dev/null +++ b/Source/Plugins/BuilderModes/Interface/ErrorCheckForm.cs @@ -0,0 +1,479 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Text; +using System.Windows.Forms; +using Microsoft.Win32; +using CodeImp.DoomBuilder.Actions; +using CodeImp.DoomBuilder.Data; +using CodeImp.DoomBuilder.Config; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Controls; +using CodeImp.DoomBuilder.Windows; +using System.Reflection; +using System.Globalization; +using System.Threading; + +#endregion + +namespace CodeImp.DoomBuilder.BuilderModes +{ + public partial class ErrorCheckForm : DelayedForm + { + #region ================== Constants + + #endregion + + #region ================== Delegates + + private delegate void CallVoidMethodDeletage(); + private delegate void CallIntMethodDelegate(int i); + private delegate void CallResultMethodDelegate(ErrorResult r); + + #endregion + + #region ================== Variables + + private volatile bool running = false; + private Thread checksthread; + private BlockMap blockmap; + + #endregion + + #region ================== Properties + + public ErrorResult SelectedResult { get { return results.SelectedItem as ErrorResult; } } + public BlockMap BlockMap { get { return blockmap; } } + + #endregion + + #region ================== Constructor / Show + + // Constructor + public ErrorCheckForm() + { + // Initialize + InitializeComponent(); + + // Find all error checkers + Type[] checkertypes = BuilderPlug.Me.FindClasses(typeof(ErrorChecker)); + foreach (Type t in checkertypes) + { + object[] attr = t.GetCustomAttributes(typeof(ErrorCheckerAttribute), true); + if (attr.Length > 0) + { + ErrorCheckerAttribute checkerattr = (attr[0] as ErrorCheckerAttribute); + + // Add the type to the checkbox list + CheckBox c = checks.Add(checkerattr.DisplayName, t); + c.Checked = checkerattr.DefaultChecked; + } + } + } + + // This shows the window + public void Show(Form owner) + { + // Move controls according to the height of the checkers box + checks.PerformLayout(); + buttoncheck.Top = checks.Bottom + 14; + resultspanel.Top = buttoncheck.Bottom + 14; + + // First time showing? + //if((this.Location.X == 0) && (this.Location.Y == 0)) + { + // Position at left-top of owner + this.Location = new Point(owner.Location.X + 20, owner.Location.Y + 90); + } + + // Close results part + resultspanel.Visible = false; + this.Size = new Size(this.Width, this.Height - this.ClientSize.Height + resultspanel.Top); + + // Show window + base.Show(owner); + } + + #endregion + + #region ================== Thread Calls + + public void SubmitResult(ErrorResult result) + { + if (results.InvokeRequired) + { + CallResultMethodDelegate d = new CallResultMethodDelegate(SubmitResult); + try { progress.Invoke(d, result); } + catch (ThreadInterruptedException) { } + } + else + { + results.Items.Add(result); + } + } + + private void SetProgressMaximum(int maximum) + { + if (progress.InvokeRequired) + { + CallIntMethodDelegate d = new CallIntMethodDelegate(SetProgressMaximum); + try { progress.Invoke(d, maximum); } + catch (ThreadInterruptedException) { } + } + else + { + progress.Maximum = maximum; + } + } + + public void AddProgressValue(int value) + { + if (progress.InvokeRequired) + { + CallIntMethodDelegate d = new CallIntMethodDelegate(AddProgressValue); + try { progress.Invoke(d, value); } + catch (ThreadInterruptedException) { } + } + else + { + progress.Value += value; + } + } + + // This stops checking (only called from the checking management thread) + private void StopChecking() + { + if (this.InvokeRequired) + { + CallVoidMethodDeletage d = new CallVoidMethodDeletage(StopChecking); + this.Invoke(d); + } + else + { + checksthread = null; + progress.Value = 0; + buttoncheck.Text = "Start Analysis"; + Cursor.Current = Cursors.Default; + running = false; + blockmap.Dispose(); + blockmap = null; + + // When no results found, show "no results" and disable the list + if (results.Items.Count == 0) + { + results.Items.Add(new ResultNoErrors()); + results.Enabled = false; + } + } + } + + // This starts checking + private void StartChecking() + { + if (running) return; + + Cursor.Current = Cursors.WaitCursor; + + // Make blockmap + RectangleF area = MapSet.CreateArea(General.Map.Map.Vertices); + area = MapSet.IncreaseArea(area, General.Map.Map.Things); + blockmap = new BlockMap(area); + blockmap.AddLinedefsSet(General.Map.Map.Linedefs); + blockmap.AddSectorsSet(General.Map.Map.Sectors); + blockmap.AddThingsSet(General.Map.Map.Things); + + // Open the results panel + this.Size = new Size(this.Width, this.Height - this.ClientSize.Height + resultspanel.Top + resultspanel.Height); + progress.Value = 0; + results.Items.Clear(); + results.Enabled = true; + ClearSelectedResult(); + resultspanel.Visible = true; + buttoncheck.Text = "Abort Analysis"; + General.Interface.RedrawDisplay(); + + // Start checking + running = true; + checksthread = new Thread(new ThreadStart(RunChecks)); + checksthread.Name = "Error Checking Management"; + checksthread.Priority = ThreadPriority.Normal; + checksthread.Start(); + + Cursor.Current = Cursors.Default; + } + + #endregion + + #region ================== Methods + + // This stops the checking + public void CloseWindow() + { + // Currently running? + if (running) + { + Cursor.Current = Cursors.WaitCursor; + checksthread.Interrupt(); + } + + ClearSelectedResult(); + this.Hide(); + } + + // This clears the selected result + private void ClearSelectedResult() + { + results.SelectedIndex = -1; + resultinfo.Text = "Select a result from the list to see more information."; + resultinfo.Enabled = false; + fix1.Visible = false; + fix2.Visible = false; + fix3.Visible = false; + } + + // This runs in a seperate thread to manage the checking threads + private void RunChecks() + { + List checkers = new List(); + List threads = new List(); + int maxthreads = Environment.ProcessorCount; + int totalprogress = 0; + int nextchecker = 0; + + // Initiate all checkers + foreach (CheckBox c in checks.Checkboxes) + { + // Include this one? + if (c.Checked) + { + Type t = (c.Tag as Type); + ErrorChecker checker = null; + + try + { + // Create instance + checker = (ErrorChecker)Assembly.GetExecutingAssembly().CreateInstance(t.FullName, false, BindingFlags.Default, null, null, CultureInfo.CurrentCulture, new object[0]); + } + catch (TargetInvocationException ex) + { + // Error! + General.ErrorLogger.Add(ErrorType.Error, "Failed to create class instance '" + t.Name + "'"); + General.WriteLogLine(ex.InnerException.GetType().Name + ": " + ex.InnerException.Message); + throw ex; + } + catch (Exception ex) + { + // Error! + General.ErrorLogger.Add(ErrorType.Error, "Failed to create class instance '" + t.Name + "'"); + General.WriteLogLine(ex.GetType().Name + ": " + ex.Message); + throw ex; + } + + // Add to list + if (checker != null) + { + checkers.Add(checker); + totalprogress += checker.TotalProgress; + } + } + } + + // Sort the checkers with highest cost first + // See CompareTo method in ErrorChecker for sorting comparison + checkers.Sort(); + + // Setup + SetProgressMaximum(totalprogress); + + // Continue while threads are running or checks are to be done + while ((nextchecker < checkers.Count) || (threads.Count > 0)) + { + // Start new thread when less than maximum number of + // threads running and there is more work to be done + while ((threads.Count < maxthreads) && (nextchecker < checkers.Count)) + { + ErrorChecker c = checkers[nextchecker++]; + Thread t = new Thread(new ThreadStart(c.Run)); + t.Name = "Error Checker '" + c.GetType().Name + "'"; + t.Priority = ThreadPriority.BelowNormal; + t.Start(); + threads.Add(t); + } + + // Remove threads that are done + for (int i = threads.Count - 1; i >= 0; i--) + if (!threads[i].IsAlive) threads.RemoveAt(i); + + // Handle thread interruption + try { Thread.Sleep(1); } + catch (ThreadInterruptedException) { break; } + } + + // Stop all running threads + foreach (Thread t in threads) + { + while (t.IsAlive) + { + try + { + t.Interrupt(); + t.Join(1); + } + catch (ThreadInterruptedException) + { + // We have to continue, we can't just leave the other threads running! + } + } + } + + // Dispose all checkers + checkers = null; + + // Done + StopChecking(); + } + + #endregion + + #region ================== Events + + // Window closing + private void ErrorCheckForm_FormClosing(object sender, FormClosingEventArgs e) + { + // If the user closes the form, then just cancel the mode + if (e.CloseReason == CloseReason.UserClosing) + { + e.Cancel = true; + General.Interface.Focus(); + General.Editing.CancelMode(); + } + } + + // Start/stop + private void buttoncheck_Click(object sender, EventArgs e) + { + // Currently running? + if (running) + { + Cursor.Current = Cursors.WaitCursor; + checksthread.Interrupt(); + } + else + { + StartChecking(); + } + } + + // Close + private void closebutton_Click(object sender, EventArgs e) + { + General.Interface.Focus(); + General.Editing.CancelMode(); + } + + // Results selection changed + private void results_SelectedIndexChanged(object sender, EventArgs e) + { + // Anything selected? + if (results.SelectedIndex >= 0) + { + ErrorResult r = (results.SelectedItem as ErrorResult); + resultinfo.Text = r.Description; + resultinfo.Enabled = true; + fix1.Text = r.Button1Text; + fix2.Text = r.Button2Text; + fix3.Text = r.Button3Text; + fix1.Visible = (r.Buttons >= 1); + fix2.Visible = (r.Buttons >= 2); + fix3.Visible = (r.Buttons >= 3); + r.ZoomToObject(); + } + else + { + ClearSelectedResult(); + } + + General.Interface.RedrawDisplay(); + } + + // First button + private void fix1_Click(object sender, EventArgs e) + { + // Anything selected? + if (results.SelectedIndex >= 0) + { + if (running) + { + General.ShowWarningMessage("You must stop the analysis before you can make changes to your map!", MessageBoxButtons.OK); + } + else + { + ErrorResult r = (results.SelectedItem as ErrorResult); + if (r.Button1Click()) StartChecking(); else General.Interface.RedrawDisplay(); + } + } + } + + // Second button + private void fix2_Click(object sender, EventArgs e) + { + // Anything selected? + if (results.SelectedIndex >= 0) + { + if (running) + { + General.ShowWarningMessage("You must stop the analysis before you can make changes to your map!", MessageBoxButtons.OK); + } + else + { + ErrorResult r = (results.SelectedItem as ErrorResult); + if (r.Button2Click()) StartChecking(); else General.Interface.RedrawDisplay(); + } + } + } + + // Third button + private void fix3_Click(object sender, EventArgs e) + { + // Anything selected? + if (results.SelectedIndex >= 0) + { + if (running) + { + General.ShowWarningMessage("You must stop the analysis before you can make changes to your map!", MessageBoxButtons.OK); + } + else + { + ErrorResult r = (results.SelectedItem as ErrorResult); + if (r.Button3Click()) StartChecking(); else General.Interface.RedrawDisplay(); + } + } + } + + private void ErrorCheckForm_HelpRequested(object sender, HelpEventArgs hlpevent) + { + General.ShowHelp("e_mapanalysis.html"); + } + + #endregion + } +} \ No newline at end of file diff --git a/Source/Plugins/BuilderModes/Interface/ErrorCheckForm.resx b/Source/Plugins/BuilderModes/Interface/ErrorCheckForm.resx new file mode 100644 index 0000000..8db63db --- /dev/null +++ b/Source/Plugins/BuilderModes/Interface/ErrorCheckForm.resx @@ -0,0 +1,153 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + \ No newline at end of file diff --git a/Source/Plugins/BuilderModes/Interface/FindReplaceForm.Designer.cs b/Source/Plugins/BuilderModes/Interface/FindReplaceForm.Designer.cs new file mode 100644 index 0000000..fdc4fcb --- /dev/null +++ b/Source/Plugins/BuilderModes/Interface/FindReplaceForm.Designer.cs @@ -0,0 +1,308 @@ +namespace CodeImp.DoomBuilder.BuilderModes +{ + partial class FindReplaceForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.Windows.Forms.Label label1; + System.Windows.Forms.Label label2; + this.replacelabel = new System.Windows.Forms.Label(); + this.searchtypes = new System.Windows.Forms.ComboBox(); + this.findinput = new System.Windows.Forms.TextBox(); + this.browsefind = new System.Windows.Forms.Button(); + this.withinselection = new System.Windows.Forms.CheckBox(); + this.browsereplace = new System.Windows.Forms.Button(); + this.replaceinput = new System.Windows.Forms.TextBox(); + this.findbutton = new System.Windows.Forms.Button(); + this.closebutton = new System.Windows.Forms.Button(); + this.resultslist = new System.Windows.Forms.ListBox(); + this.resultscount = new System.Windows.Forms.Label(); + this.resultspanel = new System.Windows.Forms.Panel(); + this.deletebutton = new System.Windows.Forms.Button(); + this.editbutton = new System.Windows.Forms.Button(); + this.groupreplace = new System.Windows.Forms.GroupBox(); + this.doreplace = new System.Windows.Forms.CheckBox(); + label1 = new System.Windows.Forms.Label(); + label2 = new System.Windows.Forms.Label(); + this.resultspanel.SuspendLayout(); + this.groupreplace.SuspendLayout(); + this.SuspendLayout(); + // + // label1 + // + label1.AutoSize = true; + label1.Location = new System.Drawing.Point(25, 15); + label1.Name = "label1"; + label1.Size = new System.Drawing.Size(69, 14); + label1.TabIndex = 0; + label1.Text = "Search type:"; + // + // label2 + // + label2.AutoSize = true; + label2.Location = new System.Drawing.Point(36, 51); + label2.Name = "label2"; + label2.Size = new System.Drawing.Size(58, 14); + label2.TabIndex = 2; + label2.Text = "Find what:"; + // + // replacelabel + // + this.replacelabel.AutoSize = true; + this.replacelabel.Location = new System.Drawing.Point(12, 28); + this.replacelabel.Name = "replacelabel"; + this.replacelabel.Size = new System.Drawing.Size(73, 14); + this.replacelabel.TabIndex = 6; + this.replacelabel.Text = "Replace with:"; + // + // searchtypes + // + this.searchtypes.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.searchtypes.FormattingEnabled = true; + this.searchtypes.Location = new System.Drawing.Point(100, 12); + this.searchtypes.Name = "searchtypes"; + this.searchtypes.Size = new System.Drawing.Size(139, 22); + this.searchtypes.Sorted = true; + this.searchtypes.TabIndex = 0; + this.searchtypes.SelectedIndexChanged += new System.EventHandler(this.searchtypes_SelectedIndexChanged); + // + // findinput + // + this.findinput.Location = new System.Drawing.Point(100, 48); + this.findinput.Name = "findinput"; + this.findinput.Size = new System.Drawing.Size(106, 20); + this.findinput.TabIndex = 1; + // + // browsefind + // + this.browsefind.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.browsefind.Image = global::CodeImp.DoomBuilder.BuilderModes.Properties.Resources.treeview; + this.browsefind.Location = new System.Drawing.Point(212, 47); + this.browsefind.Name = "browsefind"; + this.browsefind.Padding = new System.Windows.Forms.Padding(0, 0, 1, 3); + this.browsefind.Size = new System.Drawing.Size(27, 23); + this.browsefind.TabIndex = 2; + this.browsefind.UseVisualStyleBackColor = true; + this.browsefind.Click += new System.EventHandler(this.browsefind_Click); + // + // withinselection + // + this.withinselection.AutoSize = true; + this.withinselection.Location = new System.Drawing.Point(100, 75); + this.withinselection.Name = "withinselection"; + this.withinselection.Size = new System.Drawing.Size(139, 18); + this.withinselection.TabIndex = 3; + this.withinselection.Text = "Within current selection"; + this.withinselection.UseVisualStyleBackColor = true; + // + // browsereplace + // + this.browsereplace.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.browsereplace.Image = global::CodeImp.DoomBuilder.BuilderModes.Properties.Resources.treeview; + this.browsereplace.Location = new System.Drawing.Point(203, 24); + this.browsereplace.Name = "browsereplace"; + this.browsereplace.Padding = new System.Windows.Forms.Padding(0, 0, 1, 3); + this.browsereplace.Size = new System.Drawing.Size(27, 23); + this.browsereplace.TabIndex = 1; + this.browsereplace.UseVisualStyleBackColor = true; + this.browsereplace.Click += new System.EventHandler(this.browsereplace_Click); + // + // replaceinput + // + this.replaceinput.Location = new System.Drawing.Point(91, 25); + this.replaceinput.Name = "replaceinput"; + this.replaceinput.Size = new System.Drawing.Size(106, 20); + this.replaceinput.TabIndex = 0; + // + // findbutton + // + this.findbutton.Location = new System.Drawing.Point(273, 12); + this.findbutton.Name = "findbutton"; + this.findbutton.Size = new System.Drawing.Size(74, 25); + this.findbutton.TabIndex = 7; + this.findbutton.Text = "Find"; + this.findbutton.UseVisualStyleBackColor = true; + this.findbutton.Click += new System.EventHandler(this.findbutton_Click); + // + // closebutton + // + this.closebutton.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.closebutton.Location = new System.Drawing.Point(273, 43); + this.closebutton.Name = "closebutton"; + this.closebutton.Size = new System.Drawing.Size(74, 25); + this.closebutton.TabIndex = 8; + this.closebutton.Text = "Close"; + this.closebutton.UseVisualStyleBackColor = true; + this.closebutton.Click += new System.EventHandler(this.closebutton_Click); + // + // resultslist + // + this.resultslist.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.resultslist.FormattingEnabled = true; + this.resultslist.IntegralHeight = false; + this.resultslist.ItemHeight = 14; + this.resultslist.Location = new System.Drawing.Point(9, 31); + this.resultslist.Margin = new System.Windows.Forms.Padding(1); + this.resultslist.Name = "resultslist"; + this.resultslist.SelectionMode = System.Windows.Forms.SelectionMode.MultiExtended; + this.resultslist.Size = new System.Drawing.Size(341, 212); + this.resultslist.TabIndex = 0; + this.resultslist.MouseUp += new System.Windows.Forms.MouseEventHandler(this.resultslist_MouseUp); + this.resultslist.MouseDoubleClick += new System.Windows.Forms.MouseEventHandler(this.resultslist_MouseDoubleClick); + this.resultslist.SelectedIndexChanged += new System.EventHandler(this.resultslist_SelectedIndexChanged); + // + // resultscount + // + this.resultscount.AutoSize = true; + this.resultscount.Location = new System.Drawing.Point(8, 11); + this.resultscount.Name = "resultscount"; + this.resultscount.Size = new System.Drawing.Size(158, 14); + this.resultscount.TabIndex = 10; + this.resultscount.Text = "1000 items have been replaced"; + // + // resultspanel + // + this.resultspanel.Controls.Add(this.deletebutton); + this.resultspanel.Controls.Add(this.editbutton); + this.resultspanel.Controls.Add(this.resultscount); + this.resultspanel.Controls.Add(this.resultslist); + this.resultspanel.Location = new System.Drawing.Point(-1, 188); + this.resultspanel.Name = "resultspanel"; + this.resultspanel.Size = new System.Drawing.Size(362, 285); + this.resultspanel.TabIndex = 6; + this.resultspanel.Visible = false; + // + // deletebutton + // + this.deletebutton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.deletebutton.Location = new System.Drawing.Point(131, 250); + this.deletebutton.Name = "deletebutton"; + this.deletebutton.Size = new System.Drawing.Size(109, 25); + this.deletebutton.TabIndex = 2; + this.deletebutton.Text = "Delete Selection"; + this.deletebutton.UseVisualStyleBackColor = true; + this.deletebutton.Click += new System.EventHandler(this.deletebutton_Click); + // + // editbutton + // + this.editbutton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.editbutton.Location = new System.Drawing.Point(9, 250); + this.editbutton.Name = "editbutton"; + this.editbutton.Size = new System.Drawing.Size(109, 25); + this.editbutton.TabIndex = 1; + this.editbutton.Text = "Edit Selection"; + this.editbutton.UseVisualStyleBackColor = true; + this.editbutton.Click += new System.EventHandler(this.editbutton_Click); + // + // groupreplace + // + this.groupreplace.Controls.Add(this.replaceinput); + this.groupreplace.Controls.Add(this.browsereplace); + this.groupreplace.Controls.Add(this.replacelabel); + this.groupreplace.Enabled = false; + this.groupreplace.Location = new System.Drawing.Point(9, 108); + this.groupreplace.Name = "groupreplace"; + this.groupreplace.Size = new System.Drawing.Size(256, 64); + this.groupreplace.TabIndex = 5; + this.groupreplace.TabStop = false; + this.groupreplace.Text = " "; + // + // doreplace + // + this.doreplace.AutoSize = true; + this.doreplace.Location = new System.Drawing.Point(24, 105); + this.doreplace.Name = "doreplace"; + this.doreplace.Size = new System.Drawing.Size(65, 18); + this.doreplace.TabIndex = 4; + this.doreplace.Text = "Replace"; + this.doreplace.UseVisualStyleBackColor = true; + this.doreplace.CheckedChanged += new System.EventHandler(this.doreplace_CheckedChanged); + // + // FindReplaceForm + // + this.AcceptButton = this.findbutton; + this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; + this.CancelButton = this.closebutton; + this.ClientSize = new System.Drawing.Size(358, 472); + this.Controls.Add(this.doreplace); + this.Controls.Add(this.groupreplace); + this.Controls.Add(this.closebutton); + this.Controls.Add(this.findbutton); + this.Controls.Add(this.withinselection); + this.Controls.Add(this.browsefind); + this.Controls.Add(this.findinput); + this.Controls.Add(label2); + this.Controls.Add(this.searchtypes); + this.Controls.Add(label1); + this.Controls.Add(this.resultspanel); + this.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedToolWindow; + this.KeyPreview = true; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "FindReplaceForm"; + this.Opacity = 0; + this.ShowIcon = false; + this.ShowInTaskbar = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.Manual; + this.Text = "Find and Replace"; + this.KeyUp += new System.Windows.Forms.KeyEventHandler(this.FindReplaceForm_KeyUp); + this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.FindReplaceForm_FormClosing); + this.HelpRequested += new System.Windows.Forms.HelpEventHandler(this.FindReplaceForm_HelpRequested); + this.KeyDown += new System.Windows.Forms.KeyEventHandler(this.FindReplaceForm_KeyDown); + this.resultspanel.ResumeLayout(false); + this.resultspanel.PerformLayout(); + this.groupreplace.ResumeLayout(false); + this.groupreplace.PerformLayout(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.ComboBox searchtypes; + private System.Windows.Forms.TextBox findinput; + private System.Windows.Forms.Button browsefind; + private System.Windows.Forms.CheckBox withinselection; + private System.Windows.Forms.Button browsereplace; + private System.Windows.Forms.TextBox replaceinput; + private System.Windows.Forms.Button findbutton; + private System.Windows.Forms.Button closebutton; + private System.Windows.Forms.ListBox resultslist; + private System.Windows.Forms.Label resultscount; + private System.Windows.Forms.Panel resultspanel; + private System.Windows.Forms.GroupBox groupreplace; + private System.Windows.Forms.CheckBox doreplace; + private System.Windows.Forms.Button editbutton; + private System.Windows.Forms.Button deletebutton; + private System.Windows.Forms.Label replacelabel; + } +} \ No newline at end of file diff --git a/Source/Plugins/BuilderModes/Interface/FindReplaceForm.cs b/Source/Plugins/BuilderModes/Interface/FindReplaceForm.cs new file mode 100644 index 0000000..49aebcb --- /dev/null +++ b/Source/Plugins/BuilderModes/Interface/FindReplaceForm.cs @@ -0,0 +1,418 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Text; +using System.Windows.Forms; +using Microsoft.Win32; +using System.Diagnostics; +using CodeImp.DoomBuilder.Actions; +using CodeImp.DoomBuilder.Data; +using CodeImp.DoomBuilder.Config; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Controls; +using CodeImp.DoomBuilder.Windows; +using System.Reflection; +using System.Globalization; + +#endregion + +namespace CodeImp.DoomBuilder.BuilderModes +{ + public partial class FindReplaceForm : DelayedForm + { + #region ================== Constants + + #endregion + + #region ================== Variables + + private FindReplaceType newfinder; + private FindReplaceType finder; + private List findtypeslist; + bool controlpressed = false; + bool shiftpressed = false; + bool suppressevents = false; + + #endregion + + #region ================== Properties + + internal FindReplaceType Finder { get { return finder; } } + + #endregion + + #region ================== Constructor + + // Constructor + public FindReplaceForm() + { + // Initialize + InitializeComponent(); + + // Find all find/replace types + Type[] findtypes = BuilderPlug.Me.FindClasses(typeof(FindReplaceType)); + findtypeslist = new List(findtypes.Length); + foreach (Type t in findtypes) + { + FindReplaceType finderinst; + object[] attr = t.GetCustomAttributes(typeof(FindReplaceAttribute), true); + if (attr.Length > 0) + { + try + { + // Create instance + finderinst = (FindReplaceType)Assembly.GetExecutingAssembly().CreateInstance(t.FullName, false, BindingFlags.Default, null, null, CultureInfo.CurrentCulture, new object[0]); + } + catch (TargetInvocationException ex) + { + // Error! + General.ErrorLogger.Add(ErrorType.Error, "Failed to create class instance '" + t.Name + "'"); + General.WriteLogLine(ex.InnerException.GetType().Name + ": " + ex.InnerException.Message); + throw ex; + } + catch (Exception ex) + { + // Error! + General.ErrorLogger.Add(ErrorType.Error, "Failed to create class instance '" + t.Name + "'"); + General.WriteLogLine(ex.GetType().Name + ": " + ex.Message); + throw ex; + } + + // Add the finder to the list + findtypeslist.Add(finderinst); + } + } + } + + #endregion + + #region ================== Events + + // Replace (un)checked + private void doreplace_CheckedChanged(object sender, EventArgs e) + { + if (doreplace.Checked) + { + findbutton.Text = "Replace"; + groupreplace.Enabled = true; + } + else + { + findbutton.Text = "Find"; + groupreplace.Enabled = false; + } + } + + // Search type selected + private void searchtypes_SelectedIndexChanged(object sender, EventArgs e) + { + // Create instance of the selected type + newfinder = (FindReplaceType)searchtypes.SelectedItem; + + // Now setup the interface + browsefind.Enabled = newfinder.Attributes.BrowseButton; + browsereplace.Enabled = newfinder.Attributes.BrowseButton; + if (!newfinder.Attributes.Replacable) doreplace.Checked = false; + doreplace.Enabled = newfinder.Attributes.Replacable; + } + + // Browse find button clicked + private void browsefind_Click(object sender, EventArgs e) + { + findinput.Text = newfinder.Browse(findinput.Text); + } + + // Browse replacement clicked + private void browsereplace_Click(object sender, EventArgs e) + { + replaceinput.Text = newfinder.Browse(replaceinput.Text); + } + + // Find / Replace clicked + private void findbutton_Click(object sender, EventArgs e) + { + // Reset results + suppressevents = true; + resultslist.Items.Clear(); + + // Hide object information + General.Interface.HideInfo(); + + // Keep the finder we used for the search + finder = newfinder; + + // Enable/disable buttons + editbutton.Enabled = false; + deletebutton.Visible = finder.AllowDelete; + deletebutton.Enabled = false; + resultslist.BeginUpdate(); + + // Perform the search / replace and show the results + if (doreplace.Checked) + { + int ticket = General.Map.UndoRedo.CreateUndo("Replace " + searchtypes.SelectedItem); + + resultslist.Items.AddRange(finder.Find(findinput.Text, withinselection.Checked, replaceinput.Text, false)); + resultscount.Text = resultslist.Items.Count + " items found and replaced."; + + // Withdraw the undo step if nothing was replaced + if (resultslist.Items.Count <= 0) + General.Map.UndoRedo.WithdrawUndo(); + } + else + { + resultslist.Items.AddRange(finder.Find(findinput.Text, withinselection.Checked, null, false)); + resultscount.Text = resultslist.Items.Count + " items found."; + } + + // Select all results + General.Map.Map.ClearAllSelected(); + for (int i = 0; i < resultslist.Items.Count; i++) + resultslist.SelectedIndices.Add(i); + + // Let the finder know about the selection + FindReplaceObject[] selection = GetSelection(); + finder.ObjectSelected(selection); + + // Open results part of window + this.Size = new Size(this.Width, this.Height - this.ClientSize.Height + resultspanel.Top + resultspanel.Height); + resultspanel.Visible = true; + resultslist.EndUpdate(); + suppressevents = false; + + // Enable/disable buttons + editbutton.Enabled = (resultslist.SelectedIndex > -1); + deletebutton.Enabled = (resultslist.SelectedIndex > -1); + + // Set correct presentation mode + General.Map.Renderer2D.SetPresentation(finder.RenderPresentation); + + // Redraw the screen, this will show the selection + General.Interface.RedrawDisplay(); + } + + // Found item selected + private void resultslist_SelectedIndexChanged(object sender, EventArgs e) + { + if (!suppressevents) + { + // Let the finder know about the selection + FindReplaceObject[] selection = GetSelection(); + finder.ObjectSelected(selection); + + // Enable/disable buttons + editbutton.Enabled = (resultslist.SelectedIndex > -1); + deletebutton.Enabled = (resultslist.SelectedIndex > -1); + + // Redraw the screen, this will show the selection + General.Interface.RedrawDisplay(); + } + } + + // Mouse released + private void resultslist_MouseUp(object sender, MouseEventArgs e) + { + // Right-clicked? + if (e.Button == MouseButtons.Right) + { + int index = resultslist.IndexFromPoint(e.Location); + if (index != ListBox.NoMatches) + { + // Select the right-clicked item, if not selected + if (!resultslist.SelectedIndices.Contains(index)) + { + if (!controlpressed && !shiftpressed) resultslist.SelectedItems.Clear(); + resultslist.SelectedIndices.Add(index); + Update(); + } + + // Edit selected objects + editbutton_Click(this, EventArgs.Empty); + } + } + + // Enable/disable buttons + editbutton.Enabled = (resultslist.SelectedIndex > -1); + deletebutton.Enabled = (resultslist.SelectedIndex > -1); + } + + // Double clicked + private void resultslist_MouseDoubleClick(object sender, MouseEventArgs e) + { + resultslist_MouseUp(sender, new MouseEventArgs(MouseButtons.Right, 1, e.X, e.Y, e.Delta)); + } + + // Window closing + private void FindReplaceForm_FormClosing(object sender, FormClosingEventArgs e) + { + // If the user closes the form, then just cancel the mode + if (e.CloseReason == CloseReason.UserClosing) + { + e.Cancel = true; + General.Interface.Focus(); + General.Editing.CancelMode(); + } + } + + // Close button clicked + private void closebutton_Click(object sender, EventArgs e) + { + General.Interface.Focus(); + General.Editing.CancelMode(); + } + + // Key pressed + private void FindReplaceForm_KeyDown(object sender, KeyEventArgs e) + { + // Keep modifiers + controlpressed = e.Control; + shiftpressed = e.Shift; + + // Enable/disable buttons + editbutton.Enabled = (resultslist.SelectedIndex > -1); + deletebutton.Enabled = (resultslist.SelectedIndex > -1); + } + + // Key released + private void FindReplaceForm_KeyUp(object sender, KeyEventArgs e) + { + // Keep modifiers + controlpressed = e.Control; + shiftpressed = e.Shift; + + // Enable/disable buttons + editbutton.Enabled = (resultslist.SelectedIndex > -1); + deletebutton.Enabled = (resultslist.SelectedIndex > -1); + } + + // Edit Selection + private void editbutton_Click(object sender, EventArgs e) + { + FindReplaceObject[] items = GetSelection(); + if (items.Length > 0) + { + // Edit selected objects + suppressevents = true; + finder.EditObjects(items); + suppressevents = false; + resultslist_SelectedIndexChanged(sender, e); + + // Redraw the screen, this will show the selection + General.Interface.RedrawDisplay(); + } + } + + // Delete Selection + private void deletebutton_Click(object sender, EventArgs e) + { + // Delete selected objects + FindReplaceObject[] items = GetSelection(); + if (items.Length > 0) + { + suppressevents = true; + finder.DeleteObjects(items); + foreach (FindReplaceObject obj in items) + { + resultslist.Items.Remove(obj); + } + suppressevents = false; + resultslist_SelectedIndexChanged(sender, e); + + // Redraw the screen, this will show the selection + General.Interface.RedrawDisplay(); + } + } + + private void FindReplaceForm_HelpRequested(object sender, HelpEventArgs hlpevent) + { + General.ShowHelp("e_findreplace.html"); + } + + #endregion + + #region ================== Methods + + // This shows the window + public void Show(Form owner) + { + // First time showing? + //if((this.Location.X == 0) && (this.Location.Y == 0)) + { + // Position at left-top of owner + this.Location = new Point(owner.Location.X + 20, owner.Location.Y + 90); + } + + // Re-fill the search types list + searchtypes.Items.Clear(); + foreach (FindReplaceType t in findtypeslist) + { + // Check if it should be visible + if (t.DetermineVisiblity()) + { + searchtypes.Items.Add(t); + } + } + + // Select previously selected item + for (int i = 0; i < searchtypes.Items.Count; i++) + { + if (searchtypes.Items[i] == newfinder) + searchtypes.SelectedIndex = i; + } + + // Select first if none was selected + if (searchtypes.SelectedIndex < 0) + searchtypes.SelectedIndex = 0; + + // Close results part + resultspanel.Visible = false; + this.Size = new Size(this.Width, this.Height - this.ClientSize.Height + resultspanel.Top); + + // Show window + base.Show(owner); + } + + // This hides the window + new public void Hide() + { + base.Hide(); + + // Clear search results + resultslist.Items.Clear(); + } + + // This returns the selected item(s) + internal FindReplaceObject[] GetSelection() + { + // Return selected objects + FindReplaceObject[] list = new FindReplaceObject[resultslist.SelectedItems.Count]; + int index = 0; + foreach (object obj in resultslist.SelectedItems) + { + list[index++] = (FindReplaceObject)obj; + } + return list; + } + + #endregion + } +} \ No newline at end of file diff --git a/Source/Plugins/BuilderModes/Interface/FindReplaceForm.resx b/Source/Plugins/BuilderModes/Interface/FindReplaceForm.resx new file mode 100644 index 0000000..4efdbc2 --- /dev/null +++ b/Source/Plugins/BuilderModes/Interface/FindReplaceForm.resx @@ -0,0 +1,183 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + True + + + False + + + True + + + False + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + \ No newline at end of file diff --git a/Source/Plugins/BuilderModes/Interface/MakeDoorForm.Designer.cs b/Source/Plugins/BuilderModes/Interface/MakeDoorForm.Designer.cs new file mode 100644 index 0000000..88a00c9 --- /dev/null +++ b/Source/Plugins/BuilderModes/Interface/MakeDoorForm.Designer.cs @@ -0,0 +1,156 @@ +namespace CodeImp.DoomBuilder.BuilderModes.Interface +{ + partial class MakeDoorForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.doortexture = new CodeImp.DoomBuilder.Controls.TextureSelectorControl(); + this.label1 = new System.Windows.Forms.Label(); + this.label2 = new System.Windows.Forms.Label(); + this.ceilingtexture = new CodeImp.DoomBuilder.Controls.FlatSelectorControl(); + this.floortexture = new CodeImp.DoomBuilder.Controls.FlatSelectorControl(); + this.label3 = new System.Windows.Forms.Label(); + this.cancel = new System.Windows.Forms.Button(); + this.apply = new System.Windows.Forms.Button(); + this.SuspendLayout(); + // + // doortexture + // + this.doortexture.Location = new System.Drawing.Point(22, 34); + this.doortexture.Name = "doortexture"; + this.doortexture.Required = false; + this.doortexture.Size = new System.Drawing.Size(96, 115); + this.doortexture.TabIndex = 0; + this.doortexture.TextureName = ""; + // + // label1 + // + this.label1.Location = new System.Drawing.Point(22, 15); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(96, 21); + this.label1.TabIndex = 1; + this.label1.Text = "Door"; + this.label1.TextAlign = System.Drawing.ContentAlignment.TopCenter; + // + // label2 + // + this.label2.Location = new System.Drawing.Point(132, 15); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(99, 21); + this.label2.TabIndex = 2; + this.label2.Text = "Ceiling"; + this.label2.TextAlign = System.Drawing.ContentAlignment.TopCenter; + // + // ceilingtexture + // + this.ceilingtexture.Location = new System.Drawing.Point(135, 34); + this.ceilingtexture.Name = "ceilingtexture"; + this.ceilingtexture.Size = new System.Drawing.Size(96, 115); + this.ceilingtexture.TabIndex = 1; + this.ceilingtexture.TextureName = ""; + // + // floortexture + // + this.floortexture.Location = new System.Drawing.Point(248, 34); + this.floortexture.Name = "floortexture"; + this.floortexture.Size = new System.Drawing.Size(96, 115); + this.floortexture.TabIndex = 2; + this.floortexture.TextureName = ""; + // + // label3 + // + this.label3.Location = new System.Drawing.Point(245, 15); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(99, 21); + this.label3.TabIndex = 4; + this.label3.Text = "Floor"; + this.label3.TextAlign = System.Drawing.ContentAlignment.TopCenter; + // + // cancel + // + this.cancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.cancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.cancel.Location = new System.Drawing.Point(187, 180); + this.cancel.Name = "cancel"; + this.cancel.Size = new System.Drawing.Size(112, 25); + this.cancel.TabIndex = 4; + this.cancel.Text = "Cancel"; + this.cancel.UseVisualStyleBackColor = true; + this.cancel.Click += new System.EventHandler(this.cancel_Click); + // + // apply + // + this.apply.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.apply.Location = new System.Drawing.Point(69, 180); + this.apply.Name = "apply"; + this.apply.Size = new System.Drawing.Size(112, 25); + this.apply.TabIndex = 3; + this.apply.Text = "OK"; + this.apply.UseVisualStyleBackColor = true; + this.apply.Click += new System.EventHandler(this.apply_Click); + // + // MakeDoorForm + // + this.AcceptButton = this.apply; + this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; + this.CancelButton = this.cancel; + this.ClientSize = new System.Drawing.Size(368, 219); + this.Controls.Add(this.cancel); + this.Controls.Add(this.apply); + this.Controls.Add(this.floortexture); + this.Controls.Add(this.label3); + this.Controls.Add(this.ceilingtexture); + this.Controls.Add(this.label2); + this.Controls.Add(this.doortexture); + this.Controls.Add(this.label1); + this.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "MakeDoorForm"; + this.Opacity = 0; + this.ShowIcon = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Make Door"; + this.HelpRequested += new System.Windows.Forms.HelpEventHandler(this.MakeDoorForm_HelpRequested); + this.ResumeLayout(false); + + } + + #endregion + + private CodeImp.DoomBuilder.Controls.TextureSelectorControl doortexture; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.Label label2; + private CodeImp.DoomBuilder.Controls.FlatSelectorControl ceilingtexture; + private CodeImp.DoomBuilder.Controls.FlatSelectorControl floortexture; + private System.Windows.Forms.Label label3; + private System.Windows.Forms.Button cancel; + private System.Windows.Forms.Button apply; + } +} \ No newline at end of file diff --git a/Source/Plugins/BuilderModes/Interface/MakeDoorForm.cs b/Source/Plugins/BuilderModes/Interface/MakeDoorForm.cs new file mode 100644 index 0000000..7c20b9e --- /dev/null +++ b/Source/Plugins/BuilderModes/Interface/MakeDoorForm.cs @@ -0,0 +1,101 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Text; +using System.Windows.Forms; +using Microsoft.Win32; +using System.Diagnostics; +using CodeImp.DoomBuilder.Actions; +using CodeImp.DoomBuilder.Data; +using CodeImp.DoomBuilder.Config; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Controls; +using CodeImp.DoomBuilder.Windows; +using System.Reflection; +using System.Globalization; + +#endregion + +namespace CodeImp.DoomBuilder.BuilderModes.Interface +{ + public partial class MakeDoorForm : DelayedForm + { + #region ================== Properties + + public string DoorTexture { get { return doortexture.TextureName; } } + public string CeilingTexture { get { return ceilingtexture.TextureName; } } + public string FloorTexture { get { return floortexture.TextureName; } } + + #endregion + + #region ================== Constructor / Show + + // Constructor + public MakeDoorForm() + { + InitializeComponent(); + } + + // This sets the properties and shows the form + public DialogResult Show(IWin32Window owner, string doortex, string ceilingtex, string floortex) + { + this.doortexture.TextureName = doortex; + this.ceilingtexture.TextureName = ceilingtex; + this.floortexture.TextureName = floortex; + return this.ShowDialog(owner); + } + + #endregion + + #region ================== Events + + // Cancel clicked + private void cancel_Click(object sender, EventArgs e) + { + this.DialogResult = DialogResult.Cancel; + this.Close(); + } + + // OK clicked + private void apply_Click(object sender, EventArgs e) + { + // No door texture selected? + if (doortexture.TextureName.Length == 0) + { + MessageBox.Show(this, "You have to select at least a texture for the door!", "Make Door", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); + doortexture.Focus(); + } + else + { + this.DialogResult = DialogResult.OK; + this.Close(); + } + } + + private void MakeDoorForm_HelpRequested(object sender, HelpEventArgs hlpevent) + { + General.ShowHelp("e_sectors.html"); + } + + #endregion + } +} \ No newline at end of file diff --git a/Source/Plugins/BuilderModes/Interface/MakeDoorForm.resx b/Source/Plugins/BuilderModes/Interface/MakeDoorForm.resx new file mode 100644 index 0000000..d7f1bc0 --- /dev/null +++ b/Source/Plugins/BuilderModes/Interface/MakeDoorForm.resx @@ -0,0 +1,147 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + \ No newline at end of file diff --git a/Source/Plugins/BuilderModes/Interface/MenusForm.Designer.cs b/Source/Plugins/BuilderModes/Interface/MenusForm.Designer.cs new file mode 100644 index 0000000..7b6c218 --- /dev/null +++ b/Source/Plugins/BuilderModes/Interface/MenusForm.Designer.cs @@ -0,0 +1,383 @@ +namespace CodeImp.DoomBuilder.BuilderModes +{ + partial class MenusForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.menustrip = new System.Windows.Forms.MenuStrip(); + this.linedefsmenu = new System.Windows.Forms.ToolStripMenuItem(); + this.selectsinglesideditem = new System.Windows.Forms.ToolStripMenuItem(); + this.selectdoublesideditem = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem4 = new System.Windows.Forms.ToolStripSeparator(); + this.fliplinedefsitem = new System.Windows.Forms.ToolStripMenuItem(); + this.flipsidedefsitem = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem1 = new System.Windows.Forms.ToolStripSeparator(); + this.curvelinedefsitem = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem3 = new System.Windows.Forms.ToolStripSeparator(); + this.splitlinedefsitem = new System.Windows.Forms.ToolStripMenuItem(); + this.sectorsmenu = new System.Windows.Forms.ToolStripMenuItem(); + this.joinsectorsitem = new System.Windows.Forms.ToolStripMenuItem(); + this.mergesectorsitem = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem2 = new System.Windows.Forms.ToolStripSeparator(); + this.globalstrip = new System.Windows.Forms.ToolStrip(); + this.manualstrip = new System.Windows.Forms.ToolStrip(); + this.separatorsectors1 = new System.Windows.Forms.ToolStripSeparator(); + this.buttonselectionnumbers = new System.Windows.Forms.ToolStripButton(); + this.buttonbrightnessgradient = new System.Windows.Forms.ToolStripButton(); + this.buttonfloorgradient = new System.Windows.Forms.ToolStripButton(); + this.buttonceilinggradient = new System.Windows.Forms.ToolStripButton(); + this.buttonflipselectionh = new System.Windows.Forms.ToolStripButton(); + this.buttonflipselectionv = new System.Windows.Forms.ToolStripButton(); + this.buttoncurvelinedefs = new System.Windows.Forms.ToolStripButton(); + this.buttoncopyproperties = new System.Windows.Forms.ToolStripButton(); + this.buttonpasteproperties = new System.Windows.Forms.ToolStripButton(); + this.seperatorcopypaste = new System.Windows.Forms.ToolStripSeparator(); + this.menustrip.SuspendLayout(); + this.manualstrip.SuspendLayout(); + this.SuspendLayout(); + // + // menustrip + // + this.menustrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.linedefsmenu, + this.sectorsmenu}); + this.menustrip.Location = new System.Drawing.Point(0, 0); + this.menustrip.Name = "menustrip"; + this.menustrip.Size = new System.Drawing.Size(423, 24); + this.menustrip.TabIndex = 0; + this.menustrip.Text = "menustrip"; + // + // linedefsmenu + // + this.linedefsmenu.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.selectsinglesideditem, + this.selectdoublesideditem, + this.toolStripMenuItem4, + this.fliplinedefsitem, + this.flipsidedefsitem, + this.toolStripMenuItem1, + this.curvelinedefsitem, + this.toolStripMenuItem3, + this.splitlinedefsitem}); + this.linedefsmenu.Name = "linedefsmenu"; + this.linedefsmenu.Size = new System.Drawing.Size(59, 20); + this.linedefsmenu.Text = "&Linedefs"; + this.linedefsmenu.Visible = false; + // + // selectsinglesideditem + // + this.selectsinglesideditem.Name = "selectsinglesideditem"; + this.selectsinglesideditem.Size = new System.Drawing.Size(202, 22); + this.selectsinglesideditem.Tag = "selectsinglesided"; + this.selectsinglesideditem.Text = "Select &Single-sided only"; + this.selectsinglesideditem.Click += new System.EventHandler(this.InvokeTaggedAction); + // + // selectdoublesideditem + // + this.selectdoublesideditem.Name = "selectdoublesideditem"; + this.selectdoublesideditem.Size = new System.Drawing.Size(202, 22); + this.selectdoublesideditem.Tag = "selectdoublesided"; + this.selectdoublesideditem.Text = "Select &Double-sided only"; + this.selectdoublesideditem.Click += new System.EventHandler(this.InvokeTaggedAction); + // + // toolStripMenuItem4 + // + this.toolStripMenuItem4.Name = "toolStripMenuItem4"; + this.toolStripMenuItem4.Size = new System.Drawing.Size(199, 6); + // + // fliplinedefsitem + // + this.fliplinedefsitem.Name = "fliplinedefsitem"; + this.fliplinedefsitem.Size = new System.Drawing.Size(202, 22); + this.fliplinedefsitem.Tag = "fliplinedefs"; + this.fliplinedefsitem.Text = "&Flip Linedefs"; + this.fliplinedefsitem.Click += new System.EventHandler(this.InvokeTaggedAction); + // + // flipsidedefsitem + // + this.flipsidedefsitem.Name = "flipsidedefsitem"; + this.flipsidedefsitem.Size = new System.Drawing.Size(202, 22); + this.flipsidedefsitem.Tag = "flipsidedefs"; + this.flipsidedefsitem.Text = "F&lip Sidedefs"; + this.flipsidedefsitem.Click += new System.EventHandler(this.InvokeTaggedAction); + // + // toolStripMenuItem1 + // + this.toolStripMenuItem1.Name = "toolStripMenuItem1"; + this.toolStripMenuItem1.Size = new System.Drawing.Size(199, 6); + // + // curvelinedefsitem + // + this.curvelinedefsitem.Name = "curvelinedefsitem"; + this.curvelinedefsitem.Size = new System.Drawing.Size(202, 22); + this.curvelinedefsitem.Tag = "curvelinesmode"; + this.curvelinedefsitem.Text = "&Curve Linedefs..."; + this.curvelinedefsitem.Click += new System.EventHandler(this.InvokeTaggedAction); + // + // toolStripMenuItem3 + // + this.toolStripMenuItem3.Name = "toolStripMenuItem3"; + this.toolStripMenuItem3.Size = new System.Drawing.Size(199, 6); + // + // splitlinedefsitem + // + this.splitlinedefsitem.Name = "splitlinedefsitem"; + this.splitlinedefsitem.Size = new System.Drawing.Size(202, 22); + this.splitlinedefsitem.Tag = "splitlinedefs"; + this.splitlinedefsitem.Text = "S&plit Linedefs"; + this.splitlinedefsitem.Click += new System.EventHandler(this.InvokeTaggedAction); + // + // sectorsmenu + // + this.sectorsmenu.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.joinsectorsitem, + this.mergesectorsitem, + this.toolStripMenuItem2}); + this.sectorsmenu.Name = "sectorsmenu"; + this.sectorsmenu.Size = new System.Drawing.Size(55, 20); + this.sectorsmenu.Text = "&Sectors"; + this.sectorsmenu.Visible = false; + // + // joinsectorsitem + // + this.joinsectorsitem.Name = "joinsectorsitem"; + this.joinsectorsitem.Size = new System.Drawing.Size(154, 22); + this.joinsectorsitem.Tag = "joinsectors"; + this.joinsectorsitem.Text = "&Join Sectors"; + this.joinsectorsitem.Click += new System.EventHandler(this.InvokeTaggedAction); + // + // mergesectorsitem + // + this.mergesectorsitem.Name = "mergesectorsitem"; + this.mergesectorsitem.Size = new System.Drawing.Size(154, 22); + this.mergesectorsitem.Tag = "mergesectors"; + this.mergesectorsitem.Text = "&Merge Sectors"; + this.mergesectorsitem.Click += new System.EventHandler(this.InvokeTaggedAction); + // + // toolStripMenuItem2 + // + this.toolStripMenuItem2.Name = "toolStripMenuItem2"; + this.toolStripMenuItem2.Size = new System.Drawing.Size(151, 6); + // + // globalstrip + // + this.globalstrip.Location = new System.Drawing.Point(0, 24); + this.globalstrip.Name = "globalstrip"; + this.globalstrip.Size = new System.Drawing.Size(423, 25); + this.globalstrip.TabIndex = 1; + this.globalstrip.Text = "toolstrip"; + // + // manualstrip + // + this.manualstrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.buttoncopyproperties, + this.buttonpasteproperties, + this.seperatorcopypaste, + this.buttonselectionnumbers, + this.separatorsectors1, + this.buttonbrightnessgradient, + this.buttonfloorgradient, + this.buttonceilinggradient, + this.buttonflipselectionh, + this.buttonflipselectionv, + this.buttoncurvelinedefs}); + this.manualstrip.Location = new System.Drawing.Point(0, 49); + this.manualstrip.Name = "manualstrip"; + this.manualstrip.Size = new System.Drawing.Size(423, 25); + this.manualstrip.TabIndex = 2; + this.manualstrip.Text = "toolStrip1"; + // + // separatorsectors1 + // + this.separatorsectors1.Margin = new System.Windows.Forms.Padding(6, 0, 6, 0); + this.separatorsectors1.Name = "separatorsectors1"; + this.separatorsectors1.Size = new System.Drawing.Size(6, 25); + // + // buttonselectionnumbers + // + this.buttonselectionnumbers.CheckOnClick = true; + this.buttonselectionnumbers.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this.buttonselectionnumbers.Image = global::CodeImp.DoomBuilder.BuilderModes.Properties.Resources.ViewSelectionIndex; + this.buttonselectionnumbers.ImageTransparentColor = System.Drawing.Color.Magenta; + this.buttonselectionnumbers.Name = "buttonselectionnumbers"; + this.buttonselectionnumbers.Size = new System.Drawing.Size(23, 22); + this.buttonselectionnumbers.Text = "View Selection Numbering"; + this.buttonselectionnumbers.Click += new System.EventHandler(this.buttonselectionnumbers_Click); + // + // buttonbrightnessgradient + // + this.buttonbrightnessgradient.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this.buttonbrightnessgradient.Image = global::CodeImp.DoomBuilder.BuilderModes.Properties.Resources.BrightnessGradient; + this.buttonbrightnessgradient.ImageScaling = System.Windows.Forms.ToolStripItemImageScaling.None; + this.buttonbrightnessgradient.ImageTransparentColor = System.Drawing.Color.Magenta; + this.buttonbrightnessgradient.Name = "buttonbrightnessgradient"; + this.buttonbrightnessgradient.Size = new System.Drawing.Size(23, 22); + this.buttonbrightnessgradient.Tag = "gradientbrightness"; + this.buttonbrightnessgradient.Text = "Make Sector Color Gradient"; + this.buttonbrightnessgradient.Click += new System.EventHandler(this.InvokeTaggedAction); + // + // buttonfloorgradient + // + this.buttonfloorgradient.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this.buttonfloorgradient.Image = global::CodeImp.DoomBuilder.BuilderModes.Properties.Resources.FloorsGradient; + this.buttonfloorgradient.ImageTransparentColor = System.Drawing.Color.Magenta; + this.buttonfloorgradient.Name = "buttonfloorgradient"; + this.buttonfloorgradient.Size = new System.Drawing.Size(23, 22); + this.buttonfloorgradient.Tag = "gradientfloors"; + this.buttonfloorgradient.Text = "Make Floor Heights Gradient"; + this.buttonfloorgradient.Click += new System.EventHandler(this.InvokeTaggedAction); + // + // buttonceilinggradient + // + this.buttonceilinggradient.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this.buttonceilinggradient.Image = global::CodeImp.DoomBuilder.BuilderModes.Properties.Resources.CeilsGradient; + this.buttonceilinggradient.ImageTransparentColor = System.Drawing.Color.Magenta; + this.buttonceilinggradient.Name = "buttonceilinggradient"; + this.buttonceilinggradient.Size = new System.Drawing.Size(23, 22); + this.buttonceilinggradient.Tag = "gradientceilings"; + this.buttonceilinggradient.Text = "Make Ceiling Heights Gradient"; + this.buttonceilinggradient.Click += new System.EventHandler(this.InvokeTaggedAction); + // + // buttonflipselectionh + // + this.buttonflipselectionh.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this.buttonflipselectionh.Image = global::CodeImp.DoomBuilder.BuilderModes.Properties.Resources.FlipSelectionH; + this.buttonflipselectionh.ImageTransparentColor = System.Drawing.Color.Magenta; + this.buttonflipselectionh.Name = "buttonflipselectionh"; + this.buttonflipselectionh.Size = new System.Drawing.Size(23, 22); + this.buttonflipselectionh.Tag = "flipselectionh"; + this.buttonflipselectionh.Text = "Flip Selection Horizontally"; + this.buttonflipselectionh.Click += new System.EventHandler(this.InvokeTaggedAction); + // + // buttonflipselectionv + // + this.buttonflipselectionv.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this.buttonflipselectionv.Image = global::CodeImp.DoomBuilder.BuilderModes.Properties.Resources.FlipSelectionV; + this.buttonflipselectionv.ImageTransparentColor = System.Drawing.Color.Magenta; + this.buttonflipselectionv.Name = "buttonflipselectionv"; + this.buttonflipselectionv.Size = new System.Drawing.Size(23, 22); + this.buttonflipselectionv.Tag = "flipselectionv"; + this.buttonflipselectionv.Text = "Flip Selection Vertically"; + this.buttonflipselectionv.Click += new System.EventHandler(this.InvokeTaggedAction); + // + // buttoncurvelinedefs + // + this.buttoncurvelinedefs.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this.buttoncurvelinedefs.Image = global::CodeImp.DoomBuilder.BuilderModes.Properties.Resources.CurveLines; + this.buttoncurvelinedefs.ImageTransparentColor = System.Drawing.Color.Magenta; + this.buttoncurvelinedefs.Name = "buttoncurvelinedefs"; + this.buttoncurvelinedefs.Size = new System.Drawing.Size(23, 22); + this.buttoncurvelinedefs.Tag = "curvelinesmode"; + this.buttoncurvelinedefs.Text = "Curve Linedefs"; + this.buttoncurvelinedefs.Click += new System.EventHandler(this.InvokeTaggedAction); + // + // buttoncopyproperties + // + this.buttoncopyproperties.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this.buttoncopyproperties.Image = global::CodeImp.DoomBuilder.BuilderModes.Properties.Resources.CopyProperties; + this.buttoncopyproperties.ImageTransparentColor = System.Drawing.Color.Magenta; + this.buttoncopyproperties.Name = "buttoncopyproperties"; + this.buttoncopyproperties.Size = new System.Drawing.Size(23, 22); + this.buttoncopyproperties.Tag = "classiccopyproperties"; + this.buttoncopyproperties.Text = "Copy Properties"; + this.buttoncopyproperties.Click += new System.EventHandler(this.InvokeTaggedAction); + // + // buttonpasteproperties + // + this.buttonpasteproperties.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this.buttonpasteproperties.Image = global::CodeImp.DoomBuilder.BuilderModes.Properties.Resources.PasteProperties; + this.buttonpasteproperties.ImageTransparentColor = System.Drawing.Color.Magenta; + this.buttonpasteproperties.Name = "buttonpasteproperties"; + this.buttonpasteproperties.Size = new System.Drawing.Size(23, 22); + this.buttonpasteproperties.Tag = "classicpasteproperties"; + this.buttonpasteproperties.Text = "Paste Properties"; + this.buttonpasteproperties.Click += new System.EventHandler(this.InvokeTaggedAction); + // + // seperatorcopypaste + // + this.seperatorcopypaste.Margin = new System.Windows.Forms.Padding(6, 0, 6, 0); + this.seperatorcopypaste.Name = "seperatorcopypaste"; + this.seperatorcopypaste.Size = new System.Drawing.Size(6, 25); + // + // MenusForm + // + this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; + this.ClientSize = new System.Drawing.Size(423, 248); + this.Controls.Add(this.manualstrip); + this.Controls.Add(this.globalstrip); + this.Controls.Add(this.menustrip); + this.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; + this.MainMenuStrip = this.menustrip; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "MenusForm"; + this.ShowIcon = false; + this.ShowInTaskbar = false; + this.Text = "MenusForm"; + this.menustrip.ResumeLayout(false); + this.menustrip.PerformLayout(); + this.manualstrip.ResumeLayout(false); + this.manualstrip.PerformLayout(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.MenuStrip menustrip; + private System.Windows.Forms.ToolStripMenuItem linedefsmenu; + private System.Windows.Forms.ToolStripMenuItem sectorsmenu; + private System.Windows.Forms.ToolStripMenuItem fliplinedefsitem; + private System.Windows.Forms.ToolStripMenuItem flipsidedefsitem; + private System.Windows.Forms.ToolStripSeparator toolStripMenuItem1; + private System.Windows.Forms.ToolStripMenuItem curvelinedefsitem; + private System.Windows.Forms.ToolStripMenuItem joinsectorsitem; + private System.Windows.Forms.ToolStripMenuItem mergesectorsitem; + private System.Windows.Forms.ToolStripSeparator toolStripMenuItem2; + private System.Windows.Forms.ToolStripSeparator toolStripMenuItem3; + private System.Windows.Forms.ToolStripMenuItem splitlinedefsitem; + private System.Windows.Forms.ToolStrip globalstrip; + private System.Windows.Forms.ToolStrip manualstrip; + private System.Windows.Forms.ToolStripButton buttonbrightnessgradient; + private System.Windows.Forms.ToolStripMenuItem selectsinglesideditem; + private System.Windows.Forms.ToolStripMenuItem selectdoublesideditem; + private System.Windows.Forms.ToolStripSeparator toolStripMenuItem4; + private System.Windows.Forms.ToolStripButton buttonflipselectionh; + private System.Windows.Forms.ToolStripButton buttonflipselectionv; + private System.Windows.Forms.ToolStripButton buttonselectionnumbers; + private System.Windows.Forms.ToolStripSeparator separatorsectors1; + private System.Windows.Forms.ToolStripButton buttonfloorgradient; + private System.Windows.Forms.ToolStripButton buttonceilinggradient; + private System.Windows.Forms.ToolStripButton buttoncurvelinedefs; + private System.Windows.Forms.ToolStripButton buttoncopyproperties; + private System.Windows.Forms.ToolStripButton buttonpasteproperties; + private System.Windows.Forms.ToolStripSeparator seperatorcopypaste; + } +} \ No newline at end of file diff --git a/Source/Plugins/BuilderModes/Interface/MenusForm.cs b/Source/Plugins/BuilderModes/Interface/MenusForm.cs new file mode 100644 index 0000000..bc48bff --- /dev/null +++ b/Source/Plugins/BuilderModes/Interface/MenusForm.cs @@ -0,0 +1,163 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using CodeImp.DoomBuilder.Windows; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Geometry; +using System.Drawing; +using CodeImp.DoomBuilder.Editing; + +#endregion + +namespace CodeImp.DoomBuilder.BuilderModes +{ + public partial class MenusForm : Form + { + #region ================== Variables + + // Menus list + private ToolStripItem[] menus; + + // Buttons list + private ToolStripItem[] buttons; + + #endregion + + #region ================== Properties + + public ToolStripMenuItem LinedefsMenu { get { return linedefsmenu; } } + public ToolStripMenuItem SectorsMenu { get { return sectorsmenu; } } + public ToolStripButton ViewSelectionNumbers { get { return buttonselectionnumbers; } } + public ToolStripSeparator SeparatorSectors1 { get { return separatorsectors1; } } + public ToolStripButton MakeGradientBrightness { get { return buttonbrightnessgradient; } } + public ToolStripButton MakeGradientFloors { get { return buttonfloorgradient; } } + public ToolStripButton MakeGradientCeilings { get { return buttonceilinggradient; } } + public ToolStripButton FlipSelectionV { get { return buttonflipselectionv; } } + public ToolStripButton FlipSelectionH { get { return buttonflipselectionh; } } + public ToolStripButton CurveLinedefs { get { return buttoncurvelinedefs; } } + public ToolStripButton CopyProperties { get { return buttoncopyproperties; } } + public ToolStripButton PasteProperties { get { return buttonpasteproperties; } } + public ToolStripSeparator SeparatorCopyPaste { get { return seperatorcopypaste; } } + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + public MenusForm() + { + // Initialize + InitializeComponent(); + + // Apply settings + buttonselectionnumbers.Checked = BuilderPlug.Me.ViewSelectionNumbers; + + // List all menus + menus = new ToolStripItem[menustrip.Items.Count]; + for (int i = 0; i < menustrip.Items.Count; i++) menus[i] = menustrip.Items[i]; + + // List all buttons + buttons = new ToolStripItem[globalstrip.Items.Count]; + for (int i = 0; i < globalstrip.Items.Count; i++) buttons[i] = globalstrip.Items[i]; + } + + #endregion + + #region ================== Methods + + // This registers with the core + public void Register() + { + // Add the menus to the core + foreach (ToolStripMenuItem m in menus) + General.Interface.AddMenu(m); + + // Add the buttons to the core + foreach (ToolStripItem b in buttons) + General.Interface.AddButton(b); + } + + // This unregisters from the core + public void Unregister() + { + // Remove the menus from the core + foreach (ToolStripMenuItem m in menus) + General.Interface.RemoveMenu(m); + + // Remove the buttons from the core + foreach (ToolStripItem b in buttons) + General.Interface.RemoveButton(b); + } + + // This hides all menus + public void HideAllMenus() + { + foreach (ToolStripMenuItem m in menus) m.Visible = false; + } + + // This hides all except one menu + public void HideAllMenusExcept(ToolStripMenuItem showthis) + { + HideAllMenus(); + showthis.Visible = true; + } + + // This shows the menu for the current editing mode + public void ShowEditingModeMenu(EditMode mode) + { + Type sourcemode = typeof(object); + if (mode != null) + { + sourcemode = mode.GetType(); + + // When in a volatile mode, check against the last stable mode + if (mode.Attributes.Volatile) sourcemode = General.Editing.PreviousStableMode; + } + + // Final decision + if (sourcemode == typeof(LinedefsMode)) HideAllMenusExcept(linedefsmenu); + else if (sourcemode == typeof(SectorsMode)) HideAllMenusExcept(sectorsmenu); + else HideAllMenus(); + } + + // This invokes an action from control event + private void InvokeTaggedAction(object sender, EventArgs e) + { + General.Interface.InvokeTaggedAction(sender, e); + } + + // View selection numbers clicked + private void buttonselectionnumbers_Click(object sender, EventArgs e) + { + BuilderPlug.Me.ViewSelectionNumbers = buttonselectionnumbers.Checked; + General.Interface.RedrawDisplay(); + } + + #endregion + } +} \ No newline at end of file diff --git a/Source/Plugins/BuilderModes/Interface/MenusForm.resx b/Source/Plugins/BuilderModes/Interface/MenusForm.resx new file mode 100644 index 0000000..54e8865 --- /dev/null +++ b/Source/Plugins/BuilderModes/Interface/MenusForm.resx @@ -0,0 +1,141 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + True + + + 17, 17 + + + True + + + 118, 17 + + + True + + + 210, 17 + + + True + + \ No newline at end of file diff --git a/Source/Plugins/BuilderModes/Interface/PreferencesForm.Designer.cs b/Source/Plugins/BuilderModes/Interface/PreferencesForm.Designer.cs new file mode 100644 index 0000000..78ab558 --- /dev/null +++ b/Source/Plugins/BuilderModes/Interface/PreferencesForm.Designer.cs @@ -0,0 +1,397 @@ +namespace CodeImp.DoomBuilder.BuilderModes +{ + partial class PreferencesForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.tabs = new System.Windows.Forms.TabControl(); + this.taboptions = new System.Windows.Forms.TabPage(); + this.groupBox3 = new System.Windows.Forms.GroupBox(); + this.visualmodeclearselection = new System.Windows.Forms.CheckBox(); + this.autoclearselection = new System.Windows.Forms.CheckBox(); + this.editnewthing = new System.Windows.Forms.CheckBox(); + this.editnewsector = new System.Windows.Forms.CheckBox(); + this.additiveselect = new System.Windows.Forms.CheckBox(); + this.groupBox2 = new System.Windows.Forms.GroupBox(); + this.splitlinedefsrange = new CodeImp.DoomBuilder.Controls.ButtonsNumericTextbox(); + this.stitchrange = new CodeImp.DoomBuilder.Controls.ButtonsNumericTextbox(); + this.highlightthingsrange = new CodeImp.DoomBuilder.Controls.ButtonsNumericTextbox(); + this.highlightrange = new CodeImp.DoomBuilder.Controls.ButtonsNumericTextbox(); + this.label8 = new System.Windows.Forms.Label(); + this.label2 = new System.Windows.Forms.Label(); + this.label3 = new System.Windows.Forms.Label(); + this.label9 = new System.Windows.Forms.Label(); + this.label5 = new System.Windows.Forms.Label(); + this.label6 = new System.Windows.Forms.Label(); + this.label4 = new System.Windows.Forms.Label(); + this.label7 = new System.Windows.Forms.Label(); + this.groupBox1 = new System.Windows.Forms.GroupBox(); + this.label1 = new System.Windows.Forms.Label(); + this.heightbysidedef = new System.Windows.Forms.ComboBox(); + this.autodragonpaste = new System.Windows.Forms.CheckBox(); + this.tabs.SuspendLayout(); + this.taboptions.SuspendLayout(); + this.groupBox3.SuspendLayout(); + this.groupBox2.SuspendLayout(); + this.groupBox1.SuspendLayout(); + this.SuspendLayout(); + // + // tabs + // + this.tabs.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.tabs.Controls.Add(this.taboptions); + this.tabs.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tabs.Location = new System.Drawing.Point(12, 12); + this.tabs.Name = "tabs"; + this.tabs.SelectedIndex = 0; + this.tabs.Size = new System.Drawing.Size(654, 424); + this.tabs.TabIndex = 0; + // + // taboptions + // + this.taboptions.Controls.Add(this.groupBox3); + this.taboptions.Controls.Add(this.groupBox2); + this.taboptions.Controls.Add(this.groupBox1); + this.taboptions.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.taboptions.Location = new System.Drawing.Point(4, 23); + this.taboptions.Name = "taboptions"; + this.taboptions.Padding = new System.Windows.Forms.Padding(3); + this.taboptions.Size = new System.Drawing.Size(646, 397); + this.taboptions.TabIndex = 0; + this.taboptions.Text = "Editing"; + this.taboptions.UseVisualStyleBackColor = true; + // + // groupBox3 + // + this.groupBox3.Controls.Add(this.autodragonpaste); + this.groupBox3.Controls.Add(this.visualmodeclearselection); + this.groupBox3.Controls.Add(this.autoclearselection); + this.groupBox3.Controls.Add(this.editnewthing); + this.groupBox3.Controls.Add(this.editnewsector); + this.groupBox3.Controls.Add(this.additiveselect); + this.groupBox3.Location = new System.Drawing.Point(308, 90); + this.groupBox3.Name = "groupBox3"; + this.groupBox3.Size = new System.Drawing.Size(332, 200); + this.groupBox3.TabIndex = 18; + this.groupBox3.TabStop = false; + this.groupBox3.Text = " Options "; + // + // visualmodeclearselection + // + this.visualmodeclearselection.AutoSize = true; + this.visualmodeclearselection.Location = new System.Drawing.Point(23, 137); + this.visualmodeclearselection.Name = "visualmodeclearselection"; + this.visualmodeclearselection.Size = new System.Drawing.Size(220, 18); + this.visualmodeclearselection.TabIndex = 5; + this.visualmodeclearselection.Text = "Automatic clear selection in Visual Mode"; + this.visualmodeclearselection.UseVisualStyleBackColor = true; + // + // autoclearselection + // + this.autoclearselection.AutoSize = true; + this.autoclearselection.Location = new System.Drawing.Point(23, 111); + this.autoclearselection.Name = "autoclearselection"; + this.autoclearselection.Size = new System.Drawing.Size(231, 18); + this.autoclearselection.TabIndex = 4; + this.autoclearselection.Text = "Automatic clear selection in Classic Modes"; + this.autoclearselection.UseVisualStyleBackColor = true; + // + // editnewthing + // + this.editnewthing.AutoSize = true; + this.editnewthing.Location = new System.Drawing.Point(23, 33); + this.editnewthing.Name = "editnewthing"; + this.editnewthing.Size = new System.Drawing.Size(256, 18); + this.editnewthing.TabIndex = 1; + this.editnewthing.Text = "Edit thing properties when inserting a new thing"; + this.editnewthing.UseVisualStyleBackColor = true; + // + // editnewsector + // + this.editnewsector.AutoSize = true; + this.editnewsector.Location = new System.Drawing.Point(23, 59); + this.editnewsector.Name = "editnewsector"; + this.editnewsector.Size = new System.Drawing.Size(271, 18); + this.editnewsector.TabIndex = 2; + this.editnewsector.Text = "Edit sector properties when drawing a new sector"; + this.editnewsector.UseVisualStyleBackColor = true; + // + // additiveselect + // + this.additiveselect.AutoSize = true; + this.additiveselect.Location = new System.Drawing.Point(23, 85); + this.additiveselect.Name = "additiveselect"; + this.additiveselect.Size = new System.Drawing.Size(211, 18); + this.additiveselect.TabIndex = 3; + this.additiveselect.Text = "Additive selecting without holding shift"; + this.additiveselect.UseVisualStyleBackColor = true; + // + // groupBox2 + // + this.groupBox2.Controls.Add(this.splitlinedefsrange); + this.groupBox2.Controls.Add(this.stitchrange); + this.groupBox2.Controls.Add(this.highlightthingsrange); + this.groupBox2.Controls.Add(this.highlightrange); + this.groupBox2.Controls.Add(this.label8); + this.groupBox2.Controls.Add(this.label2); + this.groupBox2.Controls.Add(this.label3); + this.groupBox2.Controls.Add(this.label9); + this.groupBox2.Controls.Add(this.label5); + this.groupBox2.Controls.Add(this.label6); + this.groupBox2.Controls.Add(this.label4); + this.groupBox2.Controls.Add(this.label7); + this.groupBox2.Location = new System.Drawing.Point(6, 90); + this.groupBox2.Name = "groupBox2"; + this.groupBox2.Size = new System.Drawing.Size(292, 200); + this.groupBox2.TabIndex = 17; + this.groupBox2.TabStop = false; + this.groupBox2.Text = " Ranges "; + // + // splitlinedefsrange + // + this.splitlinedefsrange.AllowDecimal = false; + this.splitlinedefsrange.AllowNegative = false; + this.splitlinedefsrange.AllowRelative = false; + this.splitlinedefsrange.ButtonStep = 5; + this.splitlinedefsrange.Location = new System.Drawing.Point(167, 152); + this.splitlinedefsrange.Name = "splitlinedefsrange"; + this.splitlinedefsrange.Size = new System.Drawing.Size(59, 24); + this.splitlinedefsrange.StepValues = null; + this.splitlinedefsrange.TabIndex = 19; + // + // stitchrange + // + this.stitchrange.AllowDecimal = false; + this.stitchrange.AllowNegative = false; + this.stitchrange.AllowRelative = false; + this.stitchrange.ButtonStep = 5; + this.stitchrange.Location = new System.Drawing.Point(167, 112); + this.stitchrange.Name = "stitchrange"; + this.stitchrange.Size = new System.Drawing.Size(59, 24); + this.stitchrange.StepValues = null; + this.stitchrange.TabIndex = 18; + // + // highlightthingsrange + // + this.highlightthingsrange.AllowDecimal = false; + this.highlightthingsrange.AllowNegative = false; + this.highlightthingsrange.AllowRelative = false; + this.highlightthingsrange.ButtonStep = 5; + this.highlightthingsrange.Location = new System.Drawing.Point(167, 72); + this.highlightthingsrange.Name = "highlightthingsrange"; + this.highlightthingsrange.Size = new System.Drawing.Size(59, 24); + this.highlightthingsrange.StepValues = null; + this.highlightthingsrange.TabIndex = 17; + // + // highlightrange + // + this.highlightrange.AllowDecimal = false; + this.highlightrange.AllowNegative = false; + this.highlightrange.AllowRelative = false; + this.highlightrange.ButtonStep = 5; + this.highlightrange.Location = new System.Drawing.Point(167, 32); + this.highlightrange.Name = "highlightrange"; + this.highlightrange.Size = new System.Drawing.Size(59, 24); + this.highlightrange.StepValues = null; + this.highlightrange.TabIndex = 16; + // + // label8 + // + this.label8.AutoSize = true; + this.label8.Location = new System.Drawing.Point(232, 157); + this.label8.Name = "label8"; + this.label8.Size = new System.Drawing.Size(35, 14); + this.label8.TabIndex = 15; + this.label8.Text = "pixels"; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(33, 117); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(117, 14); + this.label2.TabIndex = 4; + this.label2.Text = "Stitch geometry within:"; + this.label2.TextAlign = System.Drawing.ContentAlignment.TopRight; + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Location = new System.Drawing.Point(232, 117); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(35, 14); + this.label3.TabIndex = 6; + this.label3.Text = "pixels"; + // + // label9 + // + this.label9.AutoSize = true; + this.label9.Location = new System.Drawing.Point(47, 157); + this.label9.Name = "label9"; + this.label9.Size = new System.Drawing.Size(103, 14); + this.label9.TabIndex = 13; + this.label9.Text = "Split linedefs within:"; + this.label9.TextAlign = System.Drawing.ContentAlignment.TopRight; + // + // label5 + // + this.label5.AutoSize = true; + this.label5.Location = new System.Drawing.Point(20, 37); + this.label5.Name = "label5"; + this.label5.Size = new System.Drawing.Size(130, 14); + this.label5.TabIndex = 7; + this.label5.Text = "Highlight geometry within:"; + this.label5.TextAlign = System.Drawing.ContentAlignment.TopRight; + // + // label6 + // + this.label6.AutoSize = true; + this.label6.Location = new System.Drawing.Point(232, 77); + this.label6.Name = "label6"; + this.label6.Size = new System.Drawing.Size(35, 14); + this.label6.TabIndex = 12; + this.label6.Text = "pixels"; + // + // label4 + // + this.label4.AutoSize = true; + this.label4.Location = new System.Drawing.Point(232, 37); + this.label4.Name = "label4"; + this.label4.Size = new System.Drawing.Size(35, 14); + this.label4.TabIndex = 9; + this.label4.Text = "pixels"; + // + // label7 + // + this.label7.AutoSize = true; + this.label7.Location = new System.Drawing.Point(36, 77); + this.label7.Name = "label7"; + this.label7.Size = new System.Drawing.Size(114, 14); + this.label7.TabIndex = 10; + this.label7.Text = "Highlight things within:"; + this.label7.TextAlign = System.Drawing.ContentAlignment.TopRight; + // + // groupBox1 + // + this.groupBox1.Controls.Add(this.label1); + this.groupBox1.Controls.Add(this.heightbysidedef); + this.groupBox1.Location = new System.Drawing.Point(6, 6); + this.groupBox1.Name = "groupBox1"; + this.groupBox1.Size = new System.Drawing.Size(634, 78); + this.groupBox1.TabIndex = 16; + this.groupBox1.TabStop = false; + this.groupBox1.Text = " Behavior "; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(29, 35); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(315, 14); + this.label1.TabIndex = 0; + this.label1.Text = "When sector height changes are used on a wall in Visual Mode:"; + this.label1.TextAlign = System.Drawing.ContentAlignment.TopRight; + // + // heightbysidedef + // + this.heightbysidedef.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.heightbysidedef.FormattingEnabled = true; + this.heightbysidedef.Items.AddRange(new object[] { + "Do nothing", + "Change the ceiling height", + "Change the floor height", + "Change both floor and ceiling height"}); + this.heightbysidedef.Location = new System.Drawing.Point(364, 32); + this.heightbysidedef.Name = "heightbysidedef"; + this.heightbysidedef.Size = new System.Drawing.Size(199, 22); + this.heightbysidedef.TabIndex = 0; + // + // autodragonpaste + // + this.autodragonpaste.AutoSize = true; + this.autodragonpaste.Location = new System.Drawing.Point(23, 163); + this.autodragonpaste.Name = "autodragonpaste"; + this.autodragonpaste.Size = new System.Drawing.Size(205, 18); + this.autodragonpaste.TabIndex = 6; + this.autodragonpaste.Text = "Drag selection automatically on paste"; + this.autodragonpaste.UseVisualStyleBackColor = true; + // + // PreferencesForm + // + this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; + this.ClientSize = new System.Drawing.Size(678, 448); + this.Controls.Add(this.tabs); + this.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "PreferencesForm"; + this.ShowIcon = false; + this.Text = "PreferencesForm"; + this.tabs.ResumeLayout(false); + this.taboptions.ResumeLayout(false); + this.groupBox3.ResumeLayout(false); + this.groupBox3.PerformLayout(); + this.groupBox2.ResumeLayout(false); + this.groupBox2.PerformLayout(); + this.groupBox1.ResumeLayout(false); + this.groupBox1.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.TabControl tabs; + private System.Windows.Forms.TabPage taboptions; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.ComboBox heightbysidedef; + private System.Windows.Forms.CheckBox editnewsector; + private System.Windows.Forms.CheckBox editnewthing; + private System.Windows.Forms.CheckBox additiveselect; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.Label label3; + private System.Windows.Forms.Label label4; + private System.Windows.Forms.Label label5; + private System.Windows.Forms.Label label6; + private System.Windows.Forms.Label label7; + private System.Windows.Forms.Label label8; + private System.Windows.Forms.Label label9; + private System.Windows.Forms.GroupBox groupBox1; + private System.Windows.Forms.GroupBox groupBox3; + private System.Windows.Forms.GroupBox groupBox2; + private System.Windows.Forms.CheckBox autoclearselection; + private System.Windows.Forms.CheckBox visualmodeclearselection; + private CodeImp.DoomBuilder.Controls.ButtonsNumericTextbox splitlinedefsrange; + private CodeImp.DoomBuilder.Controls.ButtonsNumericTextbox stitchrange; + private CodeImp.DoomBuilder.Controls.ButtonsNumericTextbox highlightthingsrange; + private CodeImp.DoomBuilder.Controls.ButtonsNumericTextbox highlightrange; + private System.Windows.Forms.CheckBox autodragonpaste; + } +} \ No newline at end of file diff --git a/Source/Plugins/BuilderModes/Interface/PreferencesForm.cs b/Source/Plugins/BuilderModes/Interface/PreferencesForm.cs new file mode 100644 index 0000000..35577ec --- /dev/null +++ b/Source/Plugins/BuilderModes/Interface/PreferencesForm.cs @@ -0,0 +1,120 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using CodeImp.DoomBuilder.Windows; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Geometry; +using System.Drawing; +using CodeImp.DoomBuilder.Editing; + +#endregion + +namespace CodeImp.DoomBuilder.BuilderModes +{ + public partial class PreferencesForm : Form + { + #region ================== Variables + + private PreferencesController controller; + + #endregion + + #region ================== Properties + + #endregion + + #region ================== Constructor / Disposer + + // Contrustor + public PreferencesForm() + { + InitializeComponent(); + + // Apply current settings to interface + heightbysidedef.SelectedIndex = General.Settings.ReadPluginSetting("changeheightbysidedef", 0); + editnewthing.Checked = General.Settings.ReadPluginSetting("editnewthing", true); + editnewsector.Checked = General.Settings.ReadPluginSetting("editnewsector", false); + additiveselect.Checked = General.Settings.ReadPluginSetting("additiveselect", false); + stitchrange.Text = General.Settings.ReadPluginSetting("stitchrange", 20).ToString(); + highlightrange.Text = General.Settings.ReadPluginSetting("highlightrange", 20).ToString(); + highlightthingsrange.Text = General.Settings.ReadPluginSetting("highlightthingsrange", 10).ToString(); + splitlinedefsrange.Text = General.Settings.ReadPluginSetting("splitlinedefsrange", 10).ToString(); + autoclearselection.Checked = BuilderPlug.Me.AutoClearSelection; + visualmodeclearselection.Checked = BuilderPlug.Me.VisualModeClearSelection; + autodragonpaste.Checked = BuilderPlug.Me.AutoDragOnPaste; + } + + #endregion + + #region ================== Events + + // When OK is pressed on the preferences dialog + public void OnAccept(PreferencesController controller) + { + // Write preferred settings + General.Settings.WritePluginSetting("changeheightbysidedef", heightbysidedef.SelectedIndex); + General.Settings.WritePluginSetting("editnewthing", editnewthing.Checked); + General.Settings.WritePluginSetting("editnewsector", editnewsector.Checked); + General.Settings.WritePluginSetting("additiveselect", additiveselect.Checked); + General.Settings.WritePluginSetting("stitchrange", stitchrange.GetResult(0)); + General.Settings.WritePluginSetting("highlightrange", highlightrange.GetResult(0)); + General.Settings.WritePluginSetting("highlightthingsrange", highlightthingsrange.GetResult(0)); + General.Settings.WritePluginSetting("splitlinedefsrange", splitlinedefsrange.GetResult(0)); + General.Settings.WritePluginSetting("autoclearselection", autoclearselection.Checked); + General.Settings.WritePluginSetting("visualmodeclearselection", visualmodeclearselection.Checked); + General.Settings.WritePluginSetting("autodragonpaste", autodragonpaste.Checked); + } + + // When Cancel is pressed on the preferences dialog + public void OnCancel(PreferencesController controller) + { + } + + #endregion + + #region ================== Methods + + // This sets up the form with the preferences controller + public void Setup(PreferencesController controller) + { + this.controller = controller; + + // Add tab pages + foreach (TabPage p in tabs.TabPages) + { + controller.AddTab(p); + } + + // Bind events + controller.OnAccept += OnAccept; + controller.OnCancel += OnCancel; + } + + #endregion + } +} \ No newline at end of file diff --git a/Source/Plugins/BuilderModes/Interface/PreferencesForm.resx b/Source/Plugins/BuilderModes/Interface/PreferencesForm.resx new file mode 100644 index 0000000..7d6f0ae --- /dev/null +++ b/Source/Plugins/BuilderModes/Interface/PreferencesForm.resx @@ -0,0 +1,198 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + \ No newline at end of file diff --git a/Source/Plugins/BuilderModes/Interface/UndoRedoPanel.Designer.cs b/Source/Plugins/BuilderModes/Interface/UndoRedoPanel.Designer.cs new file mode 100644 index 0000000..070ea28 --- /dev/null +++ b/Source/Plugins/BuilderModes/Interface/UndoRedoPanel.Designer.cs @@ -0,0 +1,76 @@ +namespace CodeImp.DoomBuilder.BuilderModes +{ + partial class UndoRedoPanel + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.list = new CodeImp.DoomBuilder.Controls.OptimizedListView(); + this.coldescription = new System.Windows.Forms.ColumnHeader(); + this.SuspendLayout(); + // + // list + // + this.list.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.coldescription}); + this.list.Dock = System.Windows.Forms.DockStyle.Fill; + this.list.FullRowSelect = true; + this.list.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.None; + this.list.Location = new System.Drawing.Point(0, 0); + this.list.MultiSelect = false; + this.list.Name = "list"; + this.list.ShowGroups = false; + this.list.Size = new System.Drawing.Size(390, 548); + this.list.TabIndex = 1; + this.list.UseCompatibleStateImageBehavior = false; + this.list.View = System.Windows.Forms.View.Details; + this.list.Resize += new System.EventHandler(this.list_Resize); + this.list.SelectedIndexChanged += new System.EventHandler(this.list_SelectedIndexChanged); + this.list.MouseUp += new System.Windows.Forms.MouseEventHandler(this.list_MouseUp); + this.list.KeyUp += new System.Windows.Forms.KeyEventHandler(this.list_KeyUp); + // + // coldescription + // + this.coldescription.Text = "Description"; + this.coldescription.Width = 238; + // + // UndoRedoPanel + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.list); + this.Name = "UndoRedoPanel"; + this.Size = new System.Drawing.Size(390, 548); + this.ResumeLayout(false); + + } + + #endregion + + private CodeImp.DoomBuilder.Controls.OptimizedListView list; + private System.Windows.Forms.ColumnHeader coldescription; + } +} diff --git a/Source/Plugins/BuilderModes/Interface/UndoRedoPanel.cs b/Source/Plugins/BuilderModes/Interface/UndoRedoPanel.cs new file mode 100644 index 0000000..a5482c5 --- /dev/null +++ b/Source/Plugins/BuilderModes/Interface/UndoRedoPanel.cs @@ -0,0 +1,320 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Text; +using System.Windows.Forms; +using Microsoft.Win32; +using CodeImp.DoomBuilder.Actions; +using CodeImp.DoomBuilder.Data; +using CodeImp.DoomBuilder.Config; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Controls; +using CodeImp.DoomBuilder.Windows; +using System.Reflection; +using System.Globalization; +using System.Threading; +using CodeImp.DoomBuilder.Editing; + +#endregion + +namespace CodeImp.DoomBuilder.BuilderModes +{ + public partial class UndoRedoPanel : UserControl + { + #region ================== Constants + + private const int MAX_DISPLAY_LEVELS = 400; + + #endregion + + #region ================== Variables + + private bool ignoreevents; + private int currentselection; + private int addindex; + private string begindescription; + + #endregion + + #region ================== Constructor + + // Constructor + public UndoRedoPanel() + { + InitializeComponent(); + } + + #endregion + + #region ================== Methods + + // This sets the description for the first item + public void SetBeginDescription(string description) + { + begindescription = description; + } + + // This refills the list + public unsafe void UpdateList() + { + ignoreevents = true; + currentselection = -1; + + // Check we have undo/redo capability + if ((General.Map == null) || General.Map.IsDisposed || (General.Map.UndoRedo == null)) + { + // No, clear the list + list.Items.Clear(); + return; + } + + // Make complete list of levels + List levels = General.Map.UndoRedo.GetUndoList(); + levels.Reverse(); + int numundos = levels.Count; + levels.AddRange(General.Map.UndoRedo.GetRedoList()); + int numredos = levels.Count - numundos; + + // Determine the offset to show items at + int offset = numundos - (MAX_DISPLAY_LEVELS >> 1); + if ((offset + MAX_DISPLAY_LEVELS) > levels.Count) offset = levels.Count - MAX_DISPLAY_LEVELS; + if (offset < 0) offset = 0; + + // Reset the list + list.SelectedItems.Clear(); + list.BeginUpdate(); + addindex = 0; + + // Add beginning + if (offset > 0) + { + // This indicates there is more above, but we don't display it + // because when the list gets too long it becomes slow + AddItem("..."); + } + else + { + // Real beginning + ListViewItem firstitem = AddItem(begindescription); + + // Are we at the first item? + if (numundos == 0) + { + // Highlight the last undo level + firstitem.BackColor = SystemColors.Highlight; + firstitem.ForeColor = SystemColors.HighlightText; + currentselection = 0; + } + else + { + // Normal undo level + firstitem.ForeColor = SystemColors.WindowText; + firstitem.BackColor = SystemColors.Window; + } + } + + // Add levels! + for (int i = offset; i < levels.Count; i++) + { + // Add no more than the MAX_DISPLAY_LEVELS + ListViewItem item; + if ((addindex - 1) == MAX_DISPLAY_LEVELS) + item = AddItem("..."); + else + item = AddItem(levels[i].Description); + + // Color item + if (i == (numundos - 1)) + { + // Highlight the last undo level + item.BackColor = SystemColors.Highlight; + item.ForeColor = SystemColors.HighlightText; + currentselection = addindex - 1; + } + else if (i >= numundos) + { + // Make gray because this is a redo level + item.ForeColor = SystemColors.GrayText; + item.BackColor = SystemColors.Control; + } + else + { + // Normal undo level + item.ForeColor = SystemColors.WindowText; + item.BackColor = SystemColors.Window; + } + + // Leave when list is full + if ((addindex - 1) > MAX_DISPLAY_LEVELS) + break; + } + + // Remove the excessive items + for (int i = list.Items.Count - 1; i >= addindex; i--) + list.Items.RemoveAt(i); + + // We must always have the "selected" item in the list + if (currentselection == -1) + throw new Exception("Where is the selection?"); + + // Make sure we can see the highlighted item + list.Items[currentselection].EnsureVisible(); + + list.EndUpdate(); + UpdateColumnSizes(); + ignoreevents = false; + } + + // This updates/adds an item in the list + private ListViewItem AddItem(string text) + { + ListViewItem item; + if (addindex < list.Items.Count) + { + item = list.Items[addindex]; + item.Text = text; + } + else + { + item = list.Items.Add(text); + } + addindex++; + return item; + } + + // This updates the list column size + private void UpdateColumnSizes() + { + // Check if a vertical scrollbar exists and adjust the column in the listbox accordingly + if ((BuilderPlug.GetWindowLong(list.Handle, BuilderPlug.GWL_STYLE) & BuilderPlug.WS_VSCROLL) != 0) + coldescription.Width = list.ClientRectangle.Width - 2; + else + coldescription.Width = list.ClientRectangle.Width - SystemInformation.VerticalScrollBarWidth - 2; + } + + #endregion + + #region ================== Events + + // When layout changes + protected override void OnLayout(LayoutEventArgs e) + { + base.OnLayout(e); + UpdateColumnSizes(); + } + + // Control resizes + private void list_Resize(object sender, EventArgs e) + { + UpdateColumnSizes(); + } + + // Item selected + private void list_SelectedIndexChanged(object sender, EventArgs e) + { + if (ignoreevents) return; + + ignoreevents = true; + + // We must have something selected + if (list.SelectedIndices.Count > 0) + { + // Not the same as last selected? + int selectedindex = list.SelectedIndices[0]; + if (selectedindex != currentselection) + { + // Recolor the elements in the list to match with the selection + list.BeginUpdate(); + foreach (ListViewItem item in list.Items) + { + if (item.Index < selectedindex) + { + // Normal undo level + item.ForeColor = SystemColors.WindowText; + item.BackColor = SystemColors.Window; + } + else if (item.Index == selectedindex) + { + // Target level + item.BackColor = SystemColors.Highlight; + item.ForeColor = SystemColors.HighlightText; + } + else + { + // Make gray because this will become a redo level + item.ForeColor = SystemColors.GrayText; + item.BackColor = SystemColors.Control; + } + } + list.EndUpdate(); + } + } + + General.Interface.FocusDisplay(); + + ignoreevents = false; + } + + // Mouse released + private void list_MouseUp(object sender, MouseEventArgs e) + { + ignoreevents = true; + + // We must have something selected + if (list.SelectedIndices.Count > 0) + { + // Not the same as last selected? + int selectedindex = list.SelectedIndices[0]; + if (selectedindex != currentselection) + { + // Perform the undo/redos, the list will be updated automatically + int delta = currentselection - selectedindex; + if (delta < 0) + General.Map.UndoRedo.PerformRedo(-delta); + else + General.Map.UndoRedo.PerformUndo(delta); + } + else + { + list.SelectedIndices.Clear(); + } + } + + General.Interface.FocusDisplay(); + + ignoreevents = false; + } + + // Key released + private void list_KeyUp(object sender, KeyEventArgs e) + { + ignoreevents = true; + + list.SelectedIndices.Clear(); + General.Interface.FocusDisplay(); + + ignoreevents = false; + } + + #endregion + } +} diff --git a/Source/Plugins/BuilderModes/Interface/UndoRedoPanel.resx b/Source/Plugins/BuilderModes/Interface/UndoRedoPanel.resx new file mode 100644 index 0000000..19dc0dd --- /dev/null +++ b/Source/Plugins/BuilderModes/Interface/UndoRedoPanel.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/Source/Plugins/BuilderModes/Properties/AssemblyInfo.cs b/Source/Plugins/BuilderModes/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..e0aa8a2 --- /dev/null +++ b/Source/Plugins/BuilderModes/Properties/AssemblyInfo.cs @@ -0,0 +1,32 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Builder Modes")] +[assembly: AssemblyDescription("Doom Builder Editing Modes")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("CodeImp")] +[assembly: AssemblyProduct("Doom Builder")] +[assembly: AssemblyCopyright("Copyright © 2007")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("30d96659-77e7-4921-a52f-a854c1a36b7d")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +[assembly: AssemblyVersion("2.0.1.0")] diff --git a/Source/Plugins/BuilderModes/Properties/Resources.Designer.cs b/Source/Plugins/BuilderModes/Properties/Resources.Designer.cs new file mode 100644 index 0000000..deff748 --- /dev/null +++ b/Source/Plugins/BuilderModes/Properties/Resources.Designer.cs @@ -0,0 +1,192 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace CodeImp.DoomBuilder.BuilderModes.Properties +{ + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources + { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() + { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager + { + get + { + if (object.ReferenceEquals(resourceMan, null)) + { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("CodeImp.DoomBuilder.BuilderModes.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture + { + get + { + return resourceCulture; + } + set + { + resourceCulture = value; + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap BrightnessGradient + { + get + { + object obj = ResourceManager.GetObject("BrightnessGradient", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap CeilsGradient + { + get + { + object obj = ResourceManager.GetObject("CeilsGradient", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap CopyProperties + { + get + { + object obj = ResourceManager.GetObject("CopyProperties", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap CurveLines + { + get + { + object obj = ResourceManager.GetObject("CurveLines", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap FlipSelectionH + { + get + { + object obj = ResourceManager.GetObject("FlipSelectionH", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap FlipSelectionV + { + get + { + object obj = ResourceManager.GetObject("FlipSelectionV", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap FloorsGradient + { + get + { + object obj = ResourceManager.GetObject("FloorsGradient", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap PasteProperties + { + get + { + object obj = ResourceManager.GetObject("PasteProperties", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap treeview + { + get + { + object obj = ResourceManager.GetObject("treeview", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap ViewSelectionIndex + { + get + { + object obj = ResourceManager.GetObject("ViewSelectionIndex", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + } +} diff --git a/Source/Plugins/BuilderModes/Properties/Resources.resx b/Source/Plugins/BuilderModes/Properties/Resources.resx new file mode 100644 index 0000000..d43155d --- /dev/null +++ b/Source/Plugins/BuilderModes/Properties/Resources.resx @@ -0,0 +1,151 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + ..\Resources\FlipSelectionH.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\ViewSelectionIndex.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\CopyProperties.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\CurveLines.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\FloorsGradient.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\treeview.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\FlipSelectionV.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\BrightnessGradient.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\CeilsGradient.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\PasteProperties.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + \ No newline at end of file diff --git a/Source/Plugins/BuilderModes/Resources/Actions.cfg b/Source/Plugins/BuilderModes/Resources/Actions.cfg new file mode 100644 index 0000000..3dca57e --- /dev/null +++ b/Source/Plugins/BuilderModes/Resources/Actions.cfg @@ -0,0 +1,734 @@ +/******************************************\ + Doom Builder Actions Configuration +\******************************************/ + +// Categories for the Controls preferences +categories +{ + drawing = "Drawing"; + linedefs = "Lines"; + sectors = "Sectors"; +} + +// This just defines which actions there are, what description they have and +// some behaviour options. The Doom Builder core will bind to these actions +// with delegates (function pointers) where you use the BeginAction and +// EndAction attributes. + +// Behaviour options: +// +// allowkeys: Allows the user to bind standard keys to this action. +// allowmouse: Allows the user to bind mouse buttons to this action. +// allowscroll: Allows the user to bind the scrollwheel to this action. +// disregardshift: This action will be triggered regardless if Shift is used. +// disregardcontrol: This action will be triggered regardless if Control is used. +// repeat: BeginAction will be called for automatic key repetition. +// +// allowkeys and allowmouse are true by default, the others are false by default. +// + +verticesmode +{ + title = "Vertices Mode"; + category = "modes"; + description = "Switches to vertices editing mode."; + allowkeys = true; + allowmouse = true; + allowscroll = true; +} + +linedefsmode +{ + title = "Linedefs Mode"; + category = "modes"; + description = "Switches to linedefs editing mode."; + allowkeys = true; + allowmouse = true; + allowscroll = true; +} + +sectorsmode +{ + title = "Sectors Mode"; + category = "modes"; + description = "Switches to sectors editing mode."; + allowkeys = true; + allowmouse = true; + allowscroll = true; +} + +thingsmode +{ + title = "Things Mode"; + category = "modes"; + description = "Switches to things editing mode."; + allowkeys = true; + allowmouse = true; + allowscroll = true; +} + +/* +triangulatormode +{ + title = "Triangulator Mode"; + category = "modes"; + description = "Debug stuff"; + allowkeys = true; + allowmouse = true; + allowscroll = true; +} +*/ + +drawlinesmode +{ + title = "Start Drawing"; + category = "drawing"; + description = "Starts drawing lines. See the Drawing category for actions available during drawing mode."; + allowkeys = true; + allowmouse = true; + allowscroll = true; +} + +drawpoint +{ + title = "Draw Vertex"; + category = "drawing"; + description = "Draws a vertex at the mousecursor position."; + allowkeys = true; + allowmouse = true; + allowscroll = true; + disregardshift = true; + disregardcontrol = true; +} + +removepoint +{ + title = "Remove Previous Vertex"; + category = "drawing"; + description = "Removes the previously drawn vertex from the drawing session."; + allowkeys = true; + allowmouse = true; + allowscroll = true; +} + +finishdraw +{ + title = "Finish Drawing"; + category = "drawing"; + description = "Finishes the drawing and creates the geometry."; + allowkeys = true; + allowmouse = true; + allowscroll = true; +} + +visualmode +{ + title = "Visual Mode"; + category = "modes"; + description = "Switches to visual editing mode."; + allowkeys = true; + allowmouse = true; + allowscroll = true; +} + +selectsinglesided +{ + title = "Select Single-sided"; + category = "linedefs"; + description = "This keeps only the single-sided lines in your selection selected."; + allowkeys = true; + allowmouse = true; + allowscroll = true; + default = 65585; +} + +selectdoublesided +{ + title = "Select Double-sided"; + category = "linedefs"; + description = "This keeps only the double-sided lines in your selection selected."; + allowkeys = true; + allowmouse = true; + allowscroll = true; + default = 65586; +} + +fliplinedefs +{ + title = "Flip Linedefs"; + category = "linedefs"; + description = "This flips the selected linedefs around and keeps sidedefs on the correct side."; + allowkeys = true; + allowmouse = true; + allowscroll = true; +} + +flipsidedefs +{ + title = "Flip Sidedefs"; + category = "linedefs"; + description = "This flips the sidedefs on the selected linedefs around, keeping the line in the same direction."; + allowkeys = true; + allowmouse = true; + allowscroll = true; +} + +curvelinesmode +{ + title = "Curve Linedefs"; + category = "linedefs"; + description = "Curves the selected linedefs with a given number of vertices and distance from the line."; + allowkeys = true; + allowmouse = true; + allowscroll = true; +} + +splitlinedefs +{ + title = "Split Linedefs"; + category = "linedefs"; + description = "Splits the selected linedefs in the middle, or splits the highlighted linedef at the mouse position."; + allowkeys = true; + allowmouse = true; + allowscroll = true; +} + +joinsectors +{ + title = "Join Sectors"; + category = "sectors"; + description = "Joins two or more selected sectors together and keeps all linedefs."; + allowkeys = true; + allowmouse = true; + allowscroll = true; +} + +mergesectors +{ + title = "Merge Sectors"; + category = "sectors"; + description = "Joins two or more selected sectors together and removes the shared linedefs."; + allowkeys = true; + allowmouse = true; + allowscroll = true; +} + +makesectormode +{ + title = "Make Sector Mode"; + category = "modes"; + description = "Switches to the Make Sector editing mode. This mode allows creating and/or fixing split sectors by clicking within a closed region."; + allowkeys = true; + allowmouse = true; + allowscroll = true; +} + +findmode +{ + title = "Find and Replace Mode"; + category = "modes"; + description = "Finds vertices, linedefs, sectors or things with a specific property, selects them and optionally replaces them with a given setting."; + allowkeys = true; + allowmouse = true; + allowscroll = true; +} + +editselectionmode +{ + title = "Edit Selection Mode"; + category = "modes"; + description = "Allows rotating, resizing and moving a selection."; + allowkeys = true; + allowmouse = true; + allowscroll = true; +} + +gradientbrightness +{ + title = "Make Color Gradient"; + category = "sectors"; + description = "Creates a color gradient over all selected sectors from the first to the last selected sector."; + allowkeys = true; + allowmouse = true; + allowscroll = true; +} + +gradientfloors +{ + title = "Make Floors Gradient"; + category = "sectors"; + description = "Creates a floor heights gradient over all selected sectors from the first to the last selected sector."; + allowkeys = true; + allowmouse = true; + allowscroll = true; +} + +gradientceilings +{ + title = "Make Ceilings Gradient"; + category = "sectors"; + description = "Creates a ceiling heights gradient over all selected sectors from the first to the last selected sector."; + allowkeys = true; + allowmouse = true; + allowscroll = true; +} + +makedoor +{ + title = "Make Door"; + category = "sectors"; + description = "Creates doors from the highlighted or selected sectors."; + allowkeys = true; + allowmouse = true; + allowscroll = true; +} + +lowerfloor8 +{ + title = "Lower Floor by 8 mp"; + category = "sectors"; + description = "Lowers the highlighted or selected floor heights by 8 mp."; + allowkeys = true; + allowmouse = true; + allowscroll = true; + repeat = true; + default = 131067; +} + +raisefloor8 +{ + title = "Raise Floor by 8 mp"; + category = "sectors"; + description = "Raises the highlighted or selected floor heights by 8 mp."; + allowkeys = true; + allowmouse = true; + allowscroll = true; + repeat = true; + default = 131066; +} + +lowerceiling8 +{ + title = "Lower Ceiling by 8 mp"; + category = "sectors"; + description = "Lowers the highlighted or selected ceiling heights by 8 mp."; + allowkeys = true; + allowmouse = true; + allowscroll = true; + repeat = true; + default = 196603; +} + +raiseceiling8 +{ + title = "Raise Ceiling by 8 mp"; + category = "sectors"; + description = "Raises the highlighted or selected ceiling heights by 8 mp."; + allowkeys = true; + allowmouse = true; + allowscroll = true; + repeat = true; + default = 196602; +} + +errorcheckmode +{ + title = "Map Analysis Mode"; + category = "modes"; + description = "Checks your map for errors and mistakes and reports the results."; + allowkeys = true; + allowmouse = true; + allowscroll = true; +} + +lowersector8 +{ + title = "Lower Floor/Ceiling by 8 mp"; + category = "visual"; + description = "Lowers the targeted or selected floors/ceilings by 8 mp. This also lowers selected or targeted things."; + allowkeys = true; + allowmouse = true; + allowscroll = true; + repeat = true; +} + +raisesector8 +{ + title = "Raise Floor/Ceiling by 8 mp"; + category = "visual"; + description = "Raises the targeted or selected floors/ceilings by 8 mp. This also raises selected or targeted things."; + allowkeys = true; + allowmouse = true; + allowscroll = true; + repeat = true; +} + +lowersector1 +{ + title = "Lower Floor/Ceiling by 1 mp"; + category = "visual"; + description = "Lowers the targeted or selected floors/ceilings by 1 mp. This also lowers selected or targeted things."; + allowkeys = true; + allowmouse = true; + allowscroll = true; + repeat = true; +} + +raisesector1 +{ + title = "Raise Floor/Ceiling by 1 mp"; + category = "visual"; + description = "Raises the targeted or selected floors/ceilings by 1 mp. This also raises selected or targeted things."; + allowkeys = true; + allowmouse = true; + allowscroll = true; + repeat = true; +} + +showvisualthings +{ + title = "Show Things"; + category = "visual"; + description = "Cycles through the different ways the things are shown in Visual Mode."; + allowkeys = true; + allowmouse = true; + allowscroll = true; +} + +movetextureleft +{ + title = "Move Texture Left by 1"; + category = "visual"; + description = "Moves the offset of the targeted or selected textures to the left by 1 pixel."; + allowkeys = true; + allowmouse = true; + allowscroll = true; + repeat = true; +} + +movetextureright +{ + title = "Move Texture Right by 1"; + category = "visual"; + description = "Moves the offset of the targeted or selected textures to the right by 1 pixel."; + allowkeys = true; + allowmouse = true; + allowscroll = true; + repeat = true; +} + +movetextureup +{ + title = "Move Texture Up by 1"; + category = "visual"; + description = "Moves the offset of the targeted or selected textures up by 1 pixel."; + allowkeys = true; + allowmouse = true; + allowscroll = true; + repeat = true; +} + +movetexturedown +{ + title = "Move Texture Down by 1"; + category = "visual"; + description = "Moves the offset of the targeted or selected textures down by 1 pixel."; + allowkeys = true; + allowmouse = true; + allowscroll = true; + repeat = true; +} + +movetextureleft8 +{ + title = "Move Texture Left by 8"; + category = "visual"; + description = "Moves the offset of the targeted or selected textures to the left by 8 pixels."; + allowkeys = true; + allowmouse = true; + allowscroll = true; + repeat = true; + default = 65573; +} + +movetextureright8 +{ + title = "Move Texture Right by 8"; + category = "visual"; + description = "Moves the offset of the targeted or selected textures to the right by 8 pixels."; + allowkeys = true; + allowmouse = true; + allowscroll = true; + repeat = true; + default = 65575; +} + +movetextureup8 +{ + title = "Move Texture Up by 8"; + category = "visual"; + description = "Moves the offset of the targeted or selected textures up by 8 pixels."; + allowkeys = true; + allowmouse = true; + allowscroll = true; + repeat = true; + default = 65574; +} + +movetexturedown8 +{ + title = "Move Texture Down by 8"; + category = "visual"; + description = "Moves the offset of the targeted or selected textures down by 8 pixels."; + allowkeys = true; + allowmouse = true; + allowscroll = true; + repeat = true; + default = 65576; +} + +textureselect +{ + title = "Select Texture"; + category = "visual"; + description = "Opens the texture browser to select a texture for the targeted or selected walls, floors or ceilings."; + allowkeys = true; + allowmouse = true; + allowscroll = true; +} + +texturecopy +{ + title = "Copy Texture"; + category = "visual"; + description = "Copies the targeted texture or flat for pasting."; + allowkeys = true; + allowmouse = true; + allowscroll = true; +} + +texturepaste +{ + title = "Paste Texture"; + category = "visual"; + description = "Pastes the copied texture onto the targeted or selected walls, floors or ceilings."; + allowkeys = true; + allowmouse = true; + allowscroll = true; +} + +lightcopy +{ + title = "Copy Sector Light"; + category = "visual"; + description = "Copies the targeted sector's light for pasting."; + allowkeys = true; + allowmouse = true; + allowscroll = true; +} + +lightpaste +{ + title = "Paste Sector Light"; + category = "visual"; + description = "Pastes the copied light onto the targeted or selected sector"; + allowkeys = true; + allowmouse = true; + allowscroll = true; +} + +//mxd +visualautoalign +{ + title = "Auto-align Textures X and Y"; + category = "visual"; + description = "Automatically aligns the neighbouring textures X and Y offsets until another texture is encountered."; + allowkeys = true; + allowmouse = true; + allowscroll = true; +} +visualautoalignx +{ + title = "Auto-align Textures X"; + category = "visual"; + description = "Automatically aligns the neighbouring textures X offsets until another texture is encountered."; + allowkeys = true; + allowmouse = true; + allowscroll = true; +} + +visualautoaligny +{ + title = "Auto-align Textures Y"; + category = "visual"; + description = "Automatically aligns the neighbouring textures Y offsets until another texture is encountered. The Y alignment only takes the ceiling height for each sidedef into account."; + allowkeys = true; + allowmouse = true; + allowscroll = true; +} + +toggleupperunpegged +{ + title = "Toggle Upper Unpegged"; + category = "visual"; + description = "Toggles the Upper Unpegged setting on the selected or targeted linedef."; + allowkeys = true; + allowmouse = true; + allowscroll = true; +} + +togglelowerunpegged +{ + title = "Toggle Lower Unpegged"; + category = "visual"; + description = "Toggles the Lower Unpegged setting on the selected or targeted linedef."; + allowkeys = true; + allowmouse = true; + allowscroll = true; +} + +togglegravity +{ + title = "Toggle Gravity"; + category = "visual"; + description = "Toggles the use of gravity while moving around in visual mode. Turn gravity off to fly around, turn gravity on to walk on the sector floors."; + allowkeys = true; + allowmouse = true; + allowscroll = true; +} + +togglelightonly +{ + title = "Toggle Light Only"; + category = "visual"; + description = "When enabled, no textures are drawn on walls, floors and ceilings and only the coloring of the sector is shown."; + allowkeys = true; + allowmouse = true; + allowscroll = true; + default = 86; // V +} + +togglehighlight +{ + title = "Toggle Highlight"; + category = "visual"; + description = "Toggles the highlight of the targeted object in visual mode."; + allowkeys = true; + allowmouse = true; + allowscroll = true; + default = 72; // H +} + +resettexture +{ + title = "Reset Texture Offsets"; + category = "visual"; + description = "Resets the texture offsets on the targeted or selected sidedef to 0, 0."; + allowkeys = true; + allowmouse = true; + allowscroll = true; +} + +texturecopyoffsets +{ + title = "Copy Offsets"; + category = "visual"; + description = "Copies the targeted texture offsets for pasting."; + allowkeys = true; + allowmouse = true; + allowscroll = true; +} + +texturepasteoffsets +{ + title = "Paste Offsets"; + category = "visual"; + description = "Pastes the copied texture offsets onto the targeted or selected walls."; + allowkeys = true; + allowmouse = true; + allowscroll = true; +} + +copyproperties +{ + title = "Copy Properties"; + category = "visual"; + description = "Copies the targeted object properties for pasting."; + allowkeys = true; + allowmouse = true; + allowscroll = true; +} + +pasteproperties +{ + title = "Paste Properties"; + category = "visual"; + description = "Pastes the copied properties onto the targeted or selected object."; + allowkeys = true; + allowmouse = true; + allowscroll = true; +} + +placevisualstart +{ + title = "Place Visual Mode Camera"; + category = "classic"; + description = "Places a new, or moves the existing, Visual Mode start thing to the mouse position. This thing will keep track where you left Visual Mode so that you return to Visual Mode in the same location."; + allowkeys = true; + allowmouse = true; + allowscroll = true; +} + +classiccopyproperties +{ + title = "Copy Properties"; + category = "classic"; + description = "Copies the properties of the highlighted or selected object for pasting."; + allowkeys = true; + allowmouse = true; + allowscroll = true; + default = 196675; // CTRL+SHIFT+C +} + +classicpasteproperties +{ + title = "Paste Properties"; + category = "classic"; + description = "Pastes the copied properties onto the highlighted or selected objects."; + allowkeys = true; + allowmouse = true; + allowscroll = true; + default = 196694; // CTRL+SHIFT+V +} + +floodfilltextures +{ + title = "Paste Texture Flood-Fill"; + category = "visual"; + description = "This allows you to flood-fill all adjacent textures or flats that are identical to the original with the copied texture."; + allowkeys = true; + allowmouse = true; + allowscroll = true; + default = 65540; +} + +flipselectionv +{ + title = "Flip Selection Vertically"; + category = "edit"; + description = "Flips the selection in Edit Selection mode vertically."; + allowkeys = true; + allowmouse = true; + allowscroll = true; +} + +flipselectionh +{ + title = "Flip Selection Horizontally"; + category = "edit"; + description = "Flips the selection in Edit Selection mode horizontally."; + allowkeys = true; + allowmouse = true; + allowscroll = true; +} + diff --git a/Source/Plugins/BuilderModes/Resources/Blockmap.png b/Source/Plugins/BuilderModes/Resources/Blockmap.png new file mode 100644 index 0000000000000000000000000000000000000000..cb008447a465fd6c4dae2e5b01a5b19389abaec0 GIT binary patch literal 171 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!6#=yXs@#Xw?AcwQSBeIx*fm;ZK886+f`vVdz zag8W(&d<$F%`0JG=+x>3YGCm2ba4!+n3J5aK+?eN@DJ94tiu5=(yT5_>}}`&J6aW7 zC}6e7l$yoxozrh_21C6>L&?OhjcuYkYl>nUvJPA*VvO9_aGk-6fkAf3zGH76HD&9KK!L(g%|JzoU{Uk}GH{(+Ax|L=Xb?0^6HlK)MIvQP~H*>LRJy8j)A z3&6O3S1KIC_@x_T{%0)<`Cqat_P=kl{eNjy$^VO&Fa8hIZp;93o}z*Le=QqzFgAAA zhhrGuGSKXQ{A_Qy=Ek;$|6jj;g%|(?X{@ZQ|3ySZ{wpXb{MXad`)_M&``^>k^M81F z`2W;YplcQv!8MoHmj8eJ_%VtB3>+LB|D~m+|Lf@J{I|Ea{~r_-^glT{`G09?>Hm(7 z4zK|r%~^R_|1Vy=NS*;X`Pu*X?b}D50kdY!`oCt)nqh4KltC`03;+O(B$`in@riH% O0000+8-><}O$g;O`uZLq8|G>wU|M$LI_P_sp$^WK9S*V79Y&iC9-T#im1z=piD;17m z{L+mv|Ff2b{4d!R``@?O{=c-UB**|`umK?FDH_QC*RoLuV`F!HIEL{p1I_-&&-R9E z_Vx8e7yty4Ae@(%_dhN!4vawxa&vS4$HvBjasA>VxMq-iSXdZ}0bo#7RrNn3BLj>< z3Mwlr|EH&?gE7bem}VgVUubCPKY|7j;$s+q>>UsWy5JwkHkgeS6&3%|($c^`e`D`T z8Bx6qgI-G54@oEpB$5oQdzC>OQ7?LvQIORnB{xmc=CWNkZh9k9BSUkoBCJ#@l+IqX z8%)O@HFdewr+uG!=gwOXeD99u`FuXl_w#w4695QQ{NZpoASkb9z-R9J{5Wyrb7B4W zMvi82^T`5C?6u$-lpkq_ppJh-pZXTXYS5D_oKkTUoP4@ zb9E-6zGK*1J4hvR0orXnlQoGojU8qfkx0ZOvtL=v{ai zeN`w<^g+yR1{VkpU|L(n=p;k0F3eGQ>4 z2pVd+4=5NvU1w;bHuYjJdI=^a|?bU#rr`*&{_4uag@_nq_I zqiG6fzPp;s!=0v^GmY(|slwI=@a0A{Sywb#?MeL7l?31nc*wUjnAz$AXdBLe4Z}Ez zk{1Yq0L7}RBJqU)O5Pm;aU8RTc$FPOj^=?!NjK-+wQL?T7@H?%Cf9Bu+NFVS0C$00000 LNkvXXu0mjfE83V@ literal 0 HcmV?d00001 diff --git a/Source/Plugins/BuilderModes/Resources/FindMode.png b/Source/Plugins/BuilderModes/Resources/FindMode.png new file mode 100644 index 0000000000000000000000000000000000000000..c108a2c0e0105e097ea2fe9aff9337f38e78058d GIT binary patch literal 679 zcmV;Y0$BZtP)4h;cQfDVT;f!Q1|bPziTJVnVSmDm(iW9OKe9rIBod`xWjVqRN)TUWAf(ub zg9=T{Op`(AGHO~2hDl}1bR}6s-IsGs=Xy*wtftg~>%ifD?wjX1=kT!3Dl5BpR+btc+KlPX>N{mSlFzWko0>ZNd1bKm;#vVsiX>L)V0N(As$)$L?3>Gr;Lx7DEYM%u1gQmd` z+MYhpqc}gkGGKuE$>dT(rVJBPKuin}N$zkGf{6vfM)Y^-aY~VXHDEyf^&wr#p$z#f z%`KG@Lh1r5Av6^}6K&VJTo;NIO#uVS%hi_>^YWCkLZ}o85x1tSV6AAM!PYlj?T!fo~%u2^HU zO>0PQT<^Q{+@1Rh1;K*mQN4AQ`AomfHuHk?%f7lld|rRRqxusANzU=C!I?CC3`z5u z2T7}5W%jPwfl{gL70u7>_OL0|D_n_|%tQZb_7y}E*~C`DOw{2&)=xZb;@8h>$JYP= N002ovPDHLkV1lPGHmU#s literal 0 HcmV?d00001 diff --git a/Source/Plugins/BuilderModes/Resources/FlipSelectionH.png b/Source/Plugins/BuilderModes/Resources/FlipSelectionH.png new file mode 100644 index 0000000000000000000000000000000000000000..2407623836166acb4f4baf68bc376dd356bc47cb GIT binary patch literal 268 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`oCO|{#S9GGLLkg|>2BR0kYI^x zM2T~LZf3NZkcv6U2@BYMtKh zFSYa}7D>bzdeAvUPVQ?&sVbj581I39mXBzBrC`oJD$f&Kdy6@)4$a_FXGkCiC KxvX2BR0kYI^x zM2T~LZf*-t#Vk1shldR&hj})1UH^N&LH367ZU#Opy`}%#6T2hk{-5=&E1~CIZcdy7o1Fsl zmN`=$itGY%di?o!F(yoZu}kwXkHS6f6KoO{tCLI{B=ik@_RRG^!SE-J;V6@99D`(o zNXLR8>{|&&bFC z|HSIUXMiz%4XCWF{Fk1d4#uIOq5lXPfNmqnE}*{w=n9biyx>UR|Loi>(hNZ6 zPutb@zinmd|M;Xh@(k$PRQG@X`;~A*Pz+dhddh#06G66sFi0^B!}zVM%KjhvwD$k3 zYYkvS0z>=}27okQ`nT)Hq)$07*qoM6N<$g3)uM ALjV8( literal 0 HcmV?d00001 diff --git a/Source/Plugins/BuilderModes/Resources/HeightsMode.png b/Source/Plugins/BuilderModes/Resources/HeightsMode.png new file mode 100644 index 0000000000000000000000000000000000000000..6a15ca98722f2180f7e966f3143d4bd949d9a888 GIT binary patch literal 379 zcmV->0fhdEP)hXqS#r7~k2hdCF^0{5Is zDTG$%LVyq{5)=%>fFaj~3E>TiR6=T@t|#ca4#QNc9W>--8tQt2rfH__t4mF`LuUT7 zhPs}hu4^mEtt3D;LERt0(ZyBy@V#Uk@A3d~lF%>;2Am-cKy+-?*_rTZb>;%}GN Z{RZ|JSZ2BR0kYI^x zM2T~LZf{hqK$oX26- z{C|J{$8Uf8tbS|L+5e4BlU%ar8Krg^o=BUJ_H;_+|I|IJI?NfC{CswH_W$R3`#H8r zE}1!h{{40hW`|kd-`xBx@A1F9yqrDJbA{U@6M4HDiO9`{fqre7cJ=>ke$Fa?bA$0( zOn5l^BG1b;-`+%~%s!a2i09xU$t5S*(>UVt4>(CbNzth@WBa&Ks$q@|x0U#0xdaha te*p#S%0mnq;S=ZBY-D&eSzU^OVdbe!Vpq%OGyy%u;OXk;vd$@?2>>p~c`*P0 literal 0 HcmV?d00001 diff --git a/Source/Plugins/BuilderModes/Resources/MapAnalysisMode.png b/Source/Plugins/BuilderModes/Resources/MapAnalysisMode.png new file mode 100644 index 0000000000000000000000000000000000000000..ad1cc7f47af06c0d2b9ab138ccf6618313232c39 GIT binary patch literal 452 zcmV;#0XzPQP)zWMDYw|Ni0%jK<^NS0>Z=HvgA%XeLrjGL-Pv_{)mk0gPb&p6C} z_*j+QRr@~yHCtni z#*$qhlp7Cx_*c5;!{4$!AAiTMfAid7-V@`5^>2=p@Ba8tz2VqD#lrP*IMYGs@|PCM z6?;Fb)*b!lweZouvfUs5C$4|8c(K5C(rq>-gx4Va{2CG>eUBpNKVS?4M!(P urOf@ORK8;t*?B>=e4jKiX@u;|1j7J4v&?c7$j7|^00002BR0kYI^x zM2T~LZfVBWBbmDA?$ z@BjPbZukEG|9}3E|Nl4Hw;W@daCMb<&ad-l&gh(EFydV!xn?3`@4x^1*RSXAOU#-Y z{OsTKudDu?KXy#*BtsF`5jQpsw^VZzlQS$11B8nw8<`nnN|rTbVBdLx924)z4*}Q$iB}r1^)I literal 0 HcmV?d00001 diff --git a/Source/Plugins/BuilderModes/Resources/PasteProperties.png b/Source/Plugins/BuilderModes/Resources/PasteProperties.png new file mode 100644 index 0000000000000000000000000000000000000000..55a76dbf5c4391842eed3bbeba008a94bff9dc62 GIT binary patch literal 631 zcmV--0*L*IP)lE+wFd02Vl{XU058%ORxWBS;v^?Ko92;_woR4EV|_hwlF*oe2xackVhD8vRem@fVbPhyVA| RxikO(002ovPDHLkV1flk7qtKY literal 0 HcmV?d00001 diff --git a/Source/Plugins/BuilderModes/Resources/SectorsMode.png b/Source/Plugins/BuilderModes/Resources/SectorsMode.png new file mode 100644 index 0000000000000000000000000000000000000000..a8044523cc8d1abb841dbd7c6e2cf3e314181bc0 GIT binary patch literal 337 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`oCO|{#S9GGLLkg|>2BR0kYI^x zM2T~LZfVBWBbmDA?$ z@BjPbZukCw#X9@n`x_gXnf0T$z4_lGY5Z^T>eRHfX(-ThB-_|yebFIpMTFRmXg4bJb{sqkIy){fyD{a;g%kW8m*Zf2VHTIzH zLNH5Vt(@sxZbBEW8| enaunmi_!azn~c!C*Y-f4GkCiCxvXpF33NqRbVF}#ZDnqB004<9jRpV!14l_jK~#90O_F;|lUEqV4`gZ< zvS^|+{!#HlbZ$r-t-{8orIbqD>6-|&Z-p4 zt=s~FZjL#XU{n$_CvMDSQ8Hx|u)m$}E8^bd$(Ov}`90@3?>Qo~9+6S!6Y6(D=~8Q@ z((2HybfYu7d?MNTR3x8#D9lxNMPg;Ih(GX?h%0LsD*Qs(O^Cd9tY36-d&d72HSsx{El%xX&NS{VVIBVpOM`- zMr>Ipdi!rA+50aqFRT%2Wz-?E8nIUPLe(uO=$4V2yu9ZaAo{B|QqPaD%Ke<>pW6ux z3?#hJMeNagn9G{2egl~x$3y?OH=#tHc|(hrj7A?}pB-c7vILTAA#`IUaw&6SKEUYs zo2ZIDR%~%RlD!$?eQB_v9c=9wJW2(z?irc9g00?Sqc0s>Sx{?rppKm0?~ zp}Q>2KdzXDi_8Xen>t|kRrt7ZgtXc*-m(46D^sSbyDEBJXD0EyS87x>i8=OwxAIRY z|Du%yy4+USb{W=ryja}hgrskqgcZH|+6*)axh$2(GE@%`bM!uMZnFCg*1E^VntE8@ z2AK_Gm>q)z&7M06D}$T9ID$piy}Y~s8piJ*6KU&V@w%`52CJPiR$Bx=@ko_(PBaEfpgL&< zFghNqij!9tYyTbHo@PR_cM%+s(yGQ+43duY2~)){!dTuZlFQq>R}?qPg)L}zdkEjz zz_KE@8hOd56$Gy-U|!_^V5Lk4uL`5>k}#Bbgnn0zSXFY)8nN-<;G(P|=E)fZug+&~ zN*?p|83Q5y*e_wFjJ9rJ*w-pz_gtLpX*8N3`3aj?{6e7<;7eH6f2))XdqPj2H2?qr M07*qoM6N<$f?bQiO#lD@ literal 0 HcmV?d00001 diff --git a/Source/Plugins/BuilderModes/Resources/TriangulatorMode.png b/Source/Plugins/BuilderModes/Resources/TriangulatorMode.png new file mode 100644 index 0000000000000000000000000000000000000000..22a883d96840937f539c6f40c82e0bfddfae8ebc GIT binary patch literal 924 zcmV;N17rM&P)_7nOQ&;lv)opaG+j_>*$N{~Q)y}_5%g*~O0pNTSFu!liOPx)E2u0#R7R#$(o9G! zou*||`VwW%1cS`db9?3ncH#Hoa?bC6p7Y%20<(`^?CBb=k%~f-bqjGIAq*ASOHj98 z19N2{hU&fXyKN3w8^D^0M@0RsoJ>p$_`IP|KVjwyl~|jzIz(QTvlMp@EykP60kAa* zFx^hlbKX1D%3o#2QGKW}$VuM~esj|ns$PE=_x?Uu=VYUg3%6`lT)q(-NFlej(` z5Jj{Se_dU8CnJKqzGtrr=8BE#{wukGTbnC|8f!g5*%OlP5w%1OQALIeZzdp=*zT+% z-$&@0v{o~}pd>>*(poO%zCGdhTWYYiyA&F0F$G;Dxr{hRyMuf^;4S3aLt=<=qMJh3 zM22`rQQYMVEd_z82(^83+DSCiOAFCJ<(`ME*Mv-F0%tKG zZ&-&WrXX2_3tcWGd(b{Hv4Ef;uouKrhs^4TYM^&7q9^xY^iL|rSRA+^70SE(NcN(2 z7eYYz5dLt^_I>AUUm?x|pLRjsZ^X_&X&CF%W2Ys88m4dvOi0kMFn2-(r%ZnpyGm3L yXNi2eiV?C82~b*B(db4y?#qIm2BR0kYI^x zM2T~LZf z!V~`qn;oSbB~BdMlG^cOvcQ3-=O<3$<6vxh*#LCVw3Nc@Cz+SU0xf6oboFyt=akR{ E01rV?=Kufz literal 0 HcmV?d00001 diff --git a/Source/Plugins/BuilderModes/Resources/ViewSelectionIndex.png b/Source/Plugins/BuilderModes/Resources/ViewSelectionIndex.png new file mode 100644 index 0000000000000000000000000000000000000000..96b7756accfc10e8e75efb45b42a1a0e46726551 GIT binary patch literal 429 zcmV;e0aE^nP)VR zdJwT=$BzG`V0U--|Gd1sk|+j%1c||T4FCzUva(77U5gcir~8Ao4g3 zIAh82e}OSSiUA5dZ2z4lxc@UTGW_3Q%!z4$gM$Oy053U+zmW}q;Uu6-Kwev`&k7NP zr2~)wptwa=3^HJbAs?Kt#Lxc!JJ1_9%^3d616_n-06IoDfRlv@>@|?>;c`6xmHChi zz?Nju4bT_i0`oxztTW*PW4Hkzr^7H(QbsW#Q(X`qGcXJfL{O4sV`Bqjq@;|V>abz} XUcyMlAMxC000000NkvXXu0mjfo>H*S literal 0 HcmV?d00001 diff --git a/Source/Plugins/BuilderModes/Resources/VisualMode.png b/Source/Plugins/BuilderModes/Resources/VisualMode.png new file mode 100644 index 0000000000000000000000000000000000000000..e528ccf315c8965df8097d52c49f67fc4b5be6e7 GIT binary patch literal 794 zcmV+#1LgdQP)N(8A*C zA0VmKgc$WCqEvc_YKKvwUq<&kpmA&o=h+v~jC@DqG*C)f5(xJtx_bvu*5UadZh8#* z>Kr1go+V z2p{Dj1SfBSf2`&v#r3!v_=G#PQfpK6kfRl@XXM`BUKC6 zgl0sP52JPlC?vhdbu55#3!IiGX!s$4uVt$?&41Hj?^Li?9l33 zMcrmw@>&7Tp3=HenpX8{Hj6vpb)10*IbGwc5ngVDl(_OQvQ=QMw>f)8h?TT|Op3-s zQit`D283Q5w@>EcPcmmu&S9RysAyL~VETsm3mQk?UG~Xb*duce<4oejpF33NqRbVF}#ZDnqB004<9jRpV!0rp8mK~#90&5}=Ol5rTv-ww@f zsfUf|GBJqsmWM$E6Z!-FS&R-H>Jot+LXZR*=sFA;1Xc(0FiXv4xMkVfW^=iTMkvm= zq+rzuLAEA4)hGg^wXE^!or`b>SI2$fd-%cc`+1&+=MjXhrk?+BXpsre*OWRJ7zpG=j$>q zZva#(6~e>A1aG;qcuI6TKX5Y?uZ35taBXmu1BaB0_Xb*#4j<%6myxw#vr47v)o3&` z<#L(e&=8@3hrZj3oVor5F(K6eCr=&aWl(?#6?ZOc*`-u{R0x8tR@rPeuLc4EN~IEh zzn^qco_iw4$ipc)`i=dahP6ucbqjRt?N}1FHOJ-m)mq;zEiFNt&4$%#rC2O7==GA% zB)Ok@kC>jP-(i!(ZESDd!}$vrX=>VWMUHj;9bBnYj;ht_=XSgON3~id6bkVomm->) ziDe2?j~W8% zkI3b4*jLvV_e;KoESEehIK2y0zb?_%-rm32G1a6{D58-_1Svm_7!4zi_;?WZG5dA~ nt#~%8V_2K~l00000NkvXXu0mjfy5(b7&tzM9Z7Ec$)kcv6UA3lBh)!*}W?V11E&)huKz;7HO_4;plaoI2a zMUFQjqB}RJ2{2xA@lb42+uz`@V_8ejq5pxLnFVj;1zHjnW^izDwf;VO`SRoi#Rpk5 z7$&fCT8gh$t2xJ-xMtRbxvyWn;V9SQNxs^0Zu#8L4t`2EE4Ostxo{$4N3lbW6Q=}2 Y>RkU sectors = mode.GetSelectedSectors(); + foreach (Sector s in sectors) s.Marked = false; + } + + // Do the fill + Tools.FloodfillFlats(this.Sector.Sector, fillceilings, oldtexturelong, newtextureimage, false); + + // Get the changed sectors + List changes = General.Map.Map.GetMarkedSectors(true); + foreach (Sector s in changes) + { + // Update the visual sector + if (mode.VisualSectorExists(s)) + { + BaseVisualSector vs = (mode.GetVisualSector(s) as BaseVisualSector); + if (fillceilings) + vs.Ceiling.Setup(); + else + vs.Floor.Setup(); + } + } + + General.Map.Data.UpdateUsedTextures(); + mode.Renderer.SetCrosshairBusy(false); + mode.ShowTargetInfo(); + } + } + } + } + + // Copy properties + public virtual void OnCopyProperties() + { + BuilderPlug.Me.CopiedSectorProps = new SectorProperties(Sector.Sector); + mode.SetActionResult("Copied sector properties."); + } + + // Paste properties + public virtual void OnPasteProperties() + { + if (BuilderPlug.Me.CopiedSectorProps != null) + { + mode.CreateUndo("Paste sector properties"); + mode.SetActionResult("Pasted sector properties."); + BuilderPlug.Me.CopiedSectorProps.Apply(Sector.Sector); + Sector.UpdateSectorGeometry(true); + mode.ShowTargetInfo(); + } + } + + // Select texture + public virtual void OnSelectTexture() + { + if (General.Interface.IsActiveWindow) + { + string oldtexture = GetTextureName(); + string newtexture = General.Interface.BrowseFlat(General.Interface, oldtexture); + if (newtexture != oldtexture) + { + mode.ApplySelectTexture(newtexture, true); + } + } + } + + // Apply Texture + public virtual void ApplyTexture(string texture) + { + mode.CreateUndo("Change flat " + texture); + SetTexture(texture); + } + + // Copy texture + public virtual void OnCopyTexture() + { + BuilderPlug.Me.CopiedFlat = GetTextureName(); + if (General.Map.Config.MixTexturesFlats) BuilderPlug.Me.CopiedTexture = GetTextureName(); + mode.SetActionResult("Copied flat " + GetTextureName() + "."); + } + + public virtual void OnPasteTexture() { } + + // villsa + public virtual void OnCopyLight() + { + Lights[] l = new Lights[5]; + l[0] = Sector.Sector.CeilColor; + l[1] = Sector.Sector.FloorColor; + l[2] = Sector.Sector.ThingColor; + l[3] = Sector.Sector.TopColor; + l[4] = Sector.Sector.LowerColor; + + BuilderPlug.Me.CopiedLights = l; + mode.SetActionResult("Copied sector lights."); + } + + // villsa + public virtual void OnPasteLight() + { + if (BuilderPlug.Me.CopiedLights != null) + { + Lights[] l = new Lights[5]; + List sectors = mode.GetSelectedSectors(); + + mode.CreateUndo("Paste light properties"); + mode.SetActionResult("Pasted light properties."); + + l = BuilderPlug.Me.CopiedLights; + foreach (Sector s in sectors) + { + s.CeilColor = l[0]; + s.FloorColor = l[1]; + s.ThingColor = l[2]; + //s.TopColor = l[3]; + //s.LowerColor = l[4]; + } + + Sector.UpdateSectorGeometry(true); + mode.ShowTargetInfo(); + } + } + + // Return texture name + public virtual string GetTextureName() { return ""; } + + // Edit button released + public virtual void OnEditEnd() + { + if (General.Interface.IsActiveWindow) + { + List sectors = mode.GetSelectedSectors(); + DialogResult result = General.Interface.ShowEditSectors(sectors); + if (result == DialogResult.OK) + { + // Rebuild sector + foreach (Sector s in sectors) + { + VisualSector vs = mode.GetVisualSector(s); + if (vs != null) + (vs as BaseVisualSector).UpdateSectorGeometry(true); + } + } + } + } + + // Sector height change + public virtual void OnChangeTargetHeight(int amount) + { + changed = true; + + ChangeHeight(amount); + + // Rebuild sector + Sector.UpdateSectorGeometry(true); + } + + // Sector brightness change + public virtual void OnChangeTargetBrightness(bool up) + { + mode.CreateUndo("Change sector brightness", UndoGroup.SectorBrightnessChange, Sector.Sector.FixedIndex); + + if (up) + Sector.Sector.Brightness = General.Map.Config.BrightnessLevels.GetNextHigher(Sector.Sector.Brightness); + else + Sector.Sector.Brightness = General.Map.Config.BrightnessLevels.GetNextLower(Sector.Sector.Brightness); + + mode.SetActionResult("Changed sector brightness to " + Sector.Sector.Brightness + "."); + + Sector.Sector.UpdateCache(); + + // Rebuild sector + Sector.UpdateSectorGeometry(false); + } + + #endregion + } +} diff --git a/Source/Plugins/BuilderModes/VisualModes/BaseVisualGeometrySidedef.cs b/Source/Plugins/BuilderModes/VisualModes/BaseVisualGeometrySidedef.cs new file mode 100644 index 0000000..7ba86a3 --- /dev/null +++ b/Source/Plugins/BuilderModes/VisualModes/BaseVisualGeometrySidedef.cs @@ -0,0 +1,726 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using CodeImp.DoomBuilder.Data; +using CodeImp.DoomBuilder.Windows; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Geometry; +using CodeImp.DoomBuilder.Editing; +using CodeImp.DoomBuilder.VisualModes; +using System.Drawing; + +#endregion + +namespace CodeImp.DoomBuilder.BuilderModes +{ + internal abstract class BaseVisualGeometrySidedef : VisualGeometry, IVisualEventReceiver + { + #region ================== Constants + + private const float DRAG_ANGLE_TOLERANCE = 0.06f; + + #endregion + + #region ================== Variables + + protected BaseVisualMode mode; + + protected float top; + protected float bottom; + protected long setuponloadedtexture; + + // UV dragging + private float dragstartanglexy; + private float dragstartanglez; + private Vector3D dragorigin; + private Vector3D deltaxy; + private Vector3D deltaz; + private int startoffsetx; + private int startoffsety; + protected bool uvdragging; + private int prevoffsetx; // We have to provide delta offsets, but I don't + private int prevoffsety; // want to calculate with delta offsets to prevent + // inaccuracy in the dragging. + // Undo/redo + private int undoticket; + + #endregion + + #region ================== Properties + + public bool IsDraggingUV { get { return uvdragging; } } + new public BaseVisualSector Sector { get { return (BaseVisualSector)base.Sector; } } + + #endregion + + #region ================== Constructor / Destructor + + // Constructor for sidedefs + public BaseVisualGeometrySidedef(BaseVisualMode mode, VisualSector vs, Sidedef sd) : base(vs, sd) + { + this.mode = mode; + this.deltaz = new Vector3D(0.0f, 0.0f, 1.0f); + this.deltaxy = (sd.Line.End.Position - sd.Line.Start.Position) * sd.Line.LengthInv; + if (!sd.IsFront) this.deltaxy = -this.deltaxy; + } + + #endregion + + #region ================== Methods + + // This performs a fast test in object picking + public override bool PickFastReject(Vector3D from, Vector3D to, Vector3D dir) + { + // Check if intersection point is between top and bottom + return (pickintersect.z >= bottom) && (pickintersect.z <= top); + } + + // This performs an accurate test for object picking + public override bool PickAccurate(Vector3D from, Vector3D to, Vector3D dir, ref float u_ray) + { + // The fast reject pass is already as accurate as it gets, + // so we just return the intersection distance here + u_ray = pickrayu; + return true; + } + + #endregion + + #region ================== Events + + // Unused + public virtual void OnEditBegin() { } + protected virtual void SetTexture(string texturename) { } + public abstract bool Setup(); + + // Insert middle texture + public virtual void OnInsert() + { + // No middle texture yet? + if (!Sidedef.MiddleRequired() && (string.IsNullOrEmpty(Sidedef.MiddleTexture) || (Sidedef.MiddleTexture[0] == '-'))) + { + // Make it now + mode.CreateUndo("Create middle texture"); + mode.SetActionResult("Created middle texture."); + General.Settings.FindDefaultDrawSettings(); + Sidedef.SetTextureMid(General.Settings.DefaultTexture); + + // Update + Sector.Changed = true; + + // Other side as well + if (string.IsNullOrEmpty(Sidedef.Other.MiddleTexture) || (Sidedef.Other.MiddleTexture[0] == '-')) + { + Sidedef.Other.SetTextureMid(General.Settings.DefaultTexture); + + // Update + VisualSector othersector = mode.GetVisualSector(Sidedef.Other.Sector); + if (othersector is BaseVisualSector) (othersector as BaseVisualSector).Changed = true; + } + } + } + + // Delete texture + public virtual void OnDelete() + { + // Remove texture + mode.CreateUndo("Delete texture"); + mode.SetActionResult("Deleted a texture."); + SetTexture("-"); + + // Update + Sector.Changed = true; + } + + // Processing + public virtual void OnProcess(double deltatime) + { + // If the texture was not loaded, but is loaded now, then re-setup geometry + if (setuponloadedtexture != 0) + { + ImageData t = General.Map.Data.GetTextureImage(setuponloadedtexture); + if (t != null) + { + if (t.IsImageLoaded) + { + setuponloadedtexture = 0; + Setup(); + } + } + } + } + + // Change target height + public virtual void OnChangeTargetHeight(int amount) + { + switch (BuilderPlug.Me.ChangeHeightBySidedef) + { + // Change ceiling + case 1: + if (!this.Sector.Ceiling.Changed) + this.Sector.Ceiling.OnChangeTargetHeight(amount); + break; + + // Change floor + case 2: + if (!this.Sector.Floor.Changed) + this.Sector.Floor.OnChangeTargetHeight(amount); + break; + + // Change both + case 3: + if (!this.Sector.Floor.Changed) + this.Sector.Floor.OnChangeTargetHeight(amount); + if (!this.Sector.Ceiling.Changed) + this.Sector.Ceiling.OnChangeTargetHeight(amount); + break; + } + } + + // Reset texture offsets + public virtual void OnResetTextureOffset() + { + mode.CreateUndo("Reset texture offsets"); + mode.SetActionResult("Texture offsets reset."); + + // Apply offsets + Sidedef.OffsetX = 0; + Sidedef.OffsetY = 0; + + // Update sidedef geometry + VisualSidedefParts parts = Sector.GetSidedefParts(Sidedef); + parts.SetupAllParts(); + } + + // Toggle upper-unpegged + public virtual void OnToggleUpperUnpegged() + { + if (this.Sidedef.Line.IsFlagSet(General.Map.Config.UpperUnpeggedFlag)) + { + // Remove flag + mode.ApplyUpperUnpegged(false); + } + else + { + // Add flag + mode.ApplyUpperUnpegged(true); + } + } + + // Toggle lower-unpegged + public virtual void OnToggleLowerUnpegged() + { + if (this.Sidedef.Line.IsFlagSet(General.Map.Config.LowerUnpeggedFlag)) + { + // Remove flag + mode.ApplyLowerUnpegged(false); + } + else + { + // Add flag + mode.ApplyLowerUnpegged(true); + } + } + + + // This sets the Upper Unpegged flag + public virtual void ApplyUpperUnpegged(bool set) + { + if (!set) + { + // Remove flag + mode.CreateUndo("Remove upper-unpegged setting"); + mode.SetActionResult("Removed upper-unpegged setting."); + this.Sidedef.Line.SetFlag(General.Map.Config.UpperUnpeggedFlag, false); + } + else + { + // Add flag + mode.CreateUndo("Set upper-unpegged setting"); + mode.SetActionResult("Set upper-unpegged setting."); + this.Sidedef.Line.SetFlag(General.Map.Config.UpperUnpeggedFlag, true); + } + + // Update sidedef geometry + VisualSidedefParts parts = Sector.GetSidedefParts(Sidedef); + parts.SetupAllParts(); + + // Update other sidedef geometry + if (Sidedef.Other != null) + { + BaseVisualSector othersector = (BaseVisualSector)mode.GetVisualSector(Sidedef.Other.Sector); + parts = othersector.GetSidedefParts(Sidedef.Other); + parts.SetupAllParts(); + } + } + + + // This sets the Lower Unpegged flag + public virtual void ApplyLowerUnpegged(bool set) + { + if (!set) + { + // Remove flag + mode.CreateUndo("Remove lower-unpegged setting"); + mode.SetActionResult("Removed lower-unpegged setting."); + this.Sidedef.Line.SetFlag(General.Map.Config.LowerUnpeggedFlag, false); + } + else + { + // Add flag + mode.CreateUndo("Set lower-unpegged setting"); + mode.SetActionResult("Set lower-unpegged setting."); + this.Sidedef.Line.SetFlag(General.Map.Config.LowerUnpeggedFlag, true); + } + + // Update sidedef geometry + VisualSidedefParts parts = Sector.GetSidedefParts(Sidedef); + parts.SetupAllParts(); + + // Update other sidedef geometry + if (Sidedef.Other != null) + { + BaseVisualSector othersector = (BaseVisualSector)mode.GetVisualSector(Sidedef.Other.Sector); + parts = othersector.GetSidedefParts(Sidedef.Other); + parts.SetupAllParts(); + } + } + + + // Flood-fill textures + public virtual void OnTextureFloodfill() + { + if (BuilderPlug.Me.CopiedTexture != null) + { + string oldtexture = GetTextureName(); + long oldtexturelong = Lump.MakeLongName(oldtexture); + string newtexture = BuilderPlug.Me.CopiedTexture; + if (newtexture != oldtexture) + { + mode.CreateUndo("Flood-fill textures with " + newtexture); + mode.SetActionResult("Flood-filled textures with " + newtexture + "."); + + mode.Renderer.SetCrosshairBusy(true); + General.Interface.RedrawDisplay(); + + // Get the texture + ImageData newtextureimage = General.Map.Data.GetTextureImage(newtexture); + if (newtextureimage != null) + { + if (mode.IsSingleSelection) + { + // Clear all marks, this will align everything it can + General.Map.Map.ClearMarkedSidedefs(false); + } + else + { + // Limit the alignment to selection only + General.Map.Map.ClearMarkedSidedefs(true); + List sides = mode.GetSelectedSidedefs(); + foreach (Sidedef sd in sides) sd.Marked = false; + } + + // Do the alignment + Tools.FloodfillTextures(this.Sidedef, oldtexturelong, newtextureimage, false); + + // Get the changed sidedefs + List changes = General.Map.Map.GetMarkedSidedefs(true); + foreach (Sidedef sd in changes) + { + // Update the parts for this sidedef! + if (mode.VisualSectorExists(sd.Sector)) + { + BaseVisualSector vs = (mode.GetVisualSector(sd.Sector) as BaseVisualSector); + VisualSidedefParts parts = vs.GetSidedefParts(sd); + parts.SetupAllParts(); + } + } + + General.Map.Data.UpdateUsedTextures(); + mode.Renderer.SetCrosshairBusy(false); + mode.ShowTargetInfo(); + } + } + } + } + + // Auto-align texture X offsets + public virtual void OnTextureAlign(bool alignx, bool aligny) + { + mode.CreateUndo("Auto-align textures"); + mode.SetActionResult("Auto-aligned textures."); + + // Make sure the texture is loaded (we need the texture size) + if (!base.Texture.IsImageLoaded) base.Texture.LoadImage(); + + if (mode.IsSingleSelection) + { + // Clear all marks, this will align everything it can + General.Map.Map.ClearMarkedSidedefs(false); + } + else + { + // Limit the alignment to selection only + General.Map.Map.ClearMarkedSidedefs(true); + List sides = mode.GetSelectedSidedefs(); + foreach (Sidedef sd in sides) sd.Marked = false; + } + + // Do the alignment + Tools.AutoAlignTextures(this.Sidedef, base.Texture, alignx, aligny, false); + + // Get the changed sidedefs + List changes = General.Map.Map.GetMarkedSidedefs(true); + foreach (Sidedef sd in changes) + { + // Update the parts for this sidedef! + if (mode.VisualSectorExists(sd.Sector)) + { + BaseVisualSector vs = (mode.GetVisualSector(sd.Sector) as BaseVisualSector); + VisualSidedefParts parts = vs.GetSidedefParts(sd); + parts.SetupAllParts(); + } + } + } + + // Select texture + public virtual void OnSelectTexture() + { + if (General.Interface.IsActiveWindow) + { + string oldtexture = GetTextureName(); + string newtexture = General.Interface.BrowseTexture(General.Interface, oldtexture); + if (newtexture != oldtexture) + { + mode.ApplySelectTexture(newtexture, false); + } + } + } + + // Apply Texture + public virtual void ApplyTexture(string texture) + { + mode.CreateUndo("Change texture " + texture); + SetTexture(texture); + } + + // Paste texture + public virtual void OnPasteTexture() + { + if (BuilderPlug.Me.CopiedTexture != null) + { + mode.CreateUndo("Paste texture " + BuilderPlug.Me.CopiedTexture); + mode.SetActionResult("Pasted texture " + BuilderPlug.Me.CopiedTexture + "."); + SetTexture(BuilderPlug.Me.CopiedTexture); + } + } + + // Paste texture offsets + public virtual void OnPasteTextureOffsets() + { + mode.CreateUndo("Paste texture offsets"); + Sidedef.OffsetX = BuilderPlug.Me.CopiedOffsets.X; + Sidedef.OffsetY = BuilderPlug.Me.CopiedOffsets.Y; + mode.SetActionResult("Pasted texture offsets " + Sidedef.OffsetX + ", " + Sidedef.OffsetY + "."); + + // Update sidedef geometry + VisualSidedefParts parts = Sector.GetSidedefParts(Sidedef); + parts.SetupAllParts(); + } + + // Copy texture + public virtual void OnCopyTexture() + { + BuilderPlug.Me.CopiedTexture = GetTextureName(); + if (General.Map.Config.MixTexturesFlats) BuilderPlug.Me.CopiedFlat = GetTextureName(); + mode.SetActionResult("Copied texture " + GetTextureName() + "."); + } + + // villsa + public virtual void OnCopyLight() + { + Lights[] l = new Lights[5]; + l[0] = Sidedef.Sector.CeilColor; + l[1] = Sidedef.Sector.FloorColor; + l[2] = Sidedef.Sector.ThingColor; + l[3] = Sidedef.Sector.TopColor; + l[4] = Sidedef.Sector.LowerColor; + + BuilderPlug.Me.CopiedLights = l; + mode.SetActionResult("Copied sector lights."); + } + + // villsa + public virtual void OnPasteLight() + { + if (BuilderPlug.Me.CopiedLights != null) + { + Lights[] l = new Lights[5]; + List sidedefs = mode.GetSelectedSidedefs(); + + mode.CreateUndo("Paste light properties"); + mode.SetActionResult("Pasted light properties."); + + l = BuilderPlug.Me.CopiedLights; + foreach (Sidedef s in sidedefs) + { + //s.Sector.CeilColor = l[0]; + //s.Sector.FloorColor = l[1]; + //s.Sector.ThingColor = l[2]; + s.Sector.TopColor = l[3]; + s.Sector.LowerColor = l[4]; + } + + Sector.UpdateSectorGeometry(true); + mode.ShowTargetInfo(); + } + } + + // Copy texture offsets + public virtual void OnCopyTextureOffsets() + { + BuilderPlug.Me.CopiedOffsets = new Point(Sidedef.OffsetX, Sidedef.OffsetY); + mode.SetActionResult("Copied texture offsets " + Sidedef.OffsetX + ", " + Sidedef.OffsetY + "."); + } + + // Copy properties + public virtual void OnCopyProperties() + { + BuilderPlug.Me.CopiedSidedefProps = new SidedefProperties(Sidedef); + mode.SetActionResult("Copied sidedef properties."); + } + + // Paste properties + public virtual void OnPasteProperties() + { + if (BuilderPlug.Me.CopiedSidedefProps != null) + { + mode.CreateUndo("Paste sidedef properties"); + mode.SetActionResult("Pasted sidedef properties."); + BuilderPlug.Me.CopiedSidedefProps.Apply(Sidedef); + + // Update sectors on both sides + BaseVisualSector front = (BaseVisualSector)mode.GetVisualSector(Sidedef.Sector); + if (front != null) front.Changed = true; + if (Sidedef.Other != null) + { + BaseVisualSector back = (BaseVisualSector)mode.GetVisualSector(Sidedef.Other.Sector); + if (back != null) back.Changed = true; + } + mode.ShowTargetInfo(); + } + } + + // Return texture name + public virtual string GetTextureName() { return ""; } + + // Select button pressed + public virtual void OnSelectBegin() + { + mode.LockTarget(); + dragstartanglexy = General.Map.VisualCamera.AngleXY; + dragstartanglez = General.Map.VisualCamera.AngleZ; + dragorigin = pickintersect; + startoffsetx = Sidedef.OffsetX; + startoffsety = Sidedef.OffsetY; + prevoffsetx = Sidedef.OffsetX; + prevoffsety = Sidedef.OffsetY; + } + + // Select button released + public virtual void OnSelectEnd() + { + mode.UnlockTarget(); + + // Was dragging? + if (uvdragging) + { + // Dragging stops now + uvdragging = false; + } + else + { + // Add/remove selection + if (this.selected) + { + this.selected = false; + mode.RemoveSelectedObject(this); + } + else + { + this.selected = true; + mode.AddSelectedObject(this); + } + } + } + + // Edit button released + public virtual void OnEditEnd() + { + if (General.Interface.IsActiveWindow) + { + List linedefs = mode.GetSelectedLinedefs(); + DialogResult result = General.Interface.ShowEditLinedefs(linedefs); + if (result == DialogResult.OK) + { + foreach (Linedef l in linedefs) + { + if (l.Front != null) + { + VisualSector vs = mode.GetVisualSector(l.Front.Sector); + if (vs != null) + (vs as BaseVisualSector).Changed = true; + } + + if (l.Back != null) + { + VisualSector vs = mode.GetVisualSector(l.Back.Sector); + if (vs != null) + (vs as BaseVisualSector).Changed = true; + } + } + } + } + } + + // Mouse moves + public virtual void OnMouseMove(MouseEventArgs e) + { + // Dragging UV? + if (uvdragging) + { + UpdateDragUV(); + } + else + { + // Select button pressed? + if (General.Actions.CheckActionActive(General.ThisAssembly, "visualselect")) + { + // Check if tolerance is exceeded to start UV dragging + float deltaxy = General.Map.VisualCamera.AngleXY - dragstartanglexy; + float deltaz = General.Map.VisualCamera.AngleZ - dragstartanglez; + if ((Math.Abs(deltaxy) + Math.Abs(deltaz)) > DRAG_ANGLE_TOLERANCE) + { + mode.PreAction(UndoGroup.TextureOffsetChange); + mode.CreateUndo("Change texture offsets"); + + // Start drag now + uvdragging = true; + mode.Renderer.ShowSelection = false; + mode.Renderer.ShowHighlight = false; + UpdateDragUV(); + } + } + } + } + + // This is called to update UV dragging + protected virtual void UpdateDragUV() + { + float u_ray; + + // Calculate intersection position + Line2D ray = new Line2D(General.Map.VisualCamera.Position, General.Map.VisualCamera.Target); + Sidedef.Line.Line.GetIntersection(ray, out u_ray); + Vector3D intersect = General.Map.VisualCamera.Position + (General.Map.VisualCamera.Target - General.Map.VisualCamera.Position) * u_ray; + + // Calculate offsets + Vector3D dragdelta = intersect - dragorigin; + Vector3D dragdeltaxy = dragdelta * deltaxy; + Vector3D dragdeltaz = dragdelta * deltaz; + float offsetx = dragdeltaxy.GetLength(); + float offsety = dragdeltaz.GetLength(); + if ((Math.Sign(dragdeltaxy.x) < 0) || (Math.Sign(dragdeltaxy.y) < 0) || (Math.Sign(dragdeltaxy.z) < 0)) offsetx = -offsetx; + if ((Math.Sign(dragdeltaz.x) < 0) || (Math.Sign(dragdeltaz.y) < 0) || (Math.Sign(dragdeltaz.z) < 0)) offsety = -offsety; + + // Apply offsets + int newoffsetx = startoffsetx - (int)Math.Round(offsetx); + int newoffsety = startoffsety + (int)Math.Round(offsety); + mode.ApplyTextureOffsetChange(prevoffsetx - newoffsetx, prevoffsety - newoffsety); + prevoffsetx = newoffsetx; + prevoffsety = newoffsety; + + mode.ShowTargetInfo(); + } + + // Sector brightness change + public virtual void OnChangeTargetBrightness(bool up) + { + if (!Sector.Changed) + { + // Change brightness + mode.CreateUndo("Change sector brightness", UndoGroup.SectorBrightnessChange, Sector.Sector.FixedIndex); + + if (up) + Sector.Sector.Brightness = General.Map.Config.BrightnessLevels.GetNextHigher(Sector.Sector.Brightness); + else + Sector.Sector.Brightness = General.Map.Config.BrightnessLevels.GetNextLower(Sector.Sector.Brightness); + + mode.SetActionResult("Changed sector brightness to " + Sector.Sector.Brightness + "."); + + Sector.Sector.UpdateCache(); + + // Rebuild sector + Sector.Changed = true; + + // Go for all things in this sector + foreach (Thing t in General.Map.Map.Things) + { + if (t.Sector == Sector.Sector) + { + if (mode.VisualThingExists(t)) + { + // Update thing + BaseVisualThing vt = (mode.GetVisualThing(t) as BaseVisualThing); + vt.Changed = true; + } + } + } + } + } + + // Texture offset change + public virtual void OnChangeTextureOffset(int horizontal, int vertical) + { + if ((General.Map.UndoRedo.NextUndo == null) || (General.Map.UndoRedo.NextUndo.TicketID != undoticket)) + undoticket = mode.CreateUndo("Change texture offsets"); + + // Apply offsets + Sidedef.OffsetX -= horizontal; + Sidedef.OffsetY -= vertical; + + mode.SetActionResult("Changed texture offsets to " + Sidedef.OffsetX + ", " + Sidedef.OffsetY + "."); + + // Update sidedef geometry + VisualSidedefParts parts = Sector.GetSidedefParts(Sidedef); + parts.SetupAllParts(); + } + + #endregion + } +} diff --git a/Source/Plugins/BuilderModes/VisualModes/BaseVisualMode.cs b/Source/Plugins/BuilderModes/VisualModes/BaseVisualMode.cs new file mode 100644 index 0000000..f915146 --- /dev/null +++ b/Source/Plugins/BuilderModes/VisualModes/BaseVisualMode.cs @@ -0,0 +1,1269 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using CodeImp.DoomBuilder.Windows; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Geometry; +using CodeImp.DoomBuilder.Editing; +using CodeImp.DoomBuilder.Actions; +using CodeImp.DoomBuilder.VisualModes; +using CodeImp.DoomBuilder.Config; + +#endregion + +namespace CodeImp.DoomBuilder.BuilderModes +{ + [EditMode(DisplayName = "Visual Mode", + SwitchAction = "visualmode", // Action name used to switch to this mode + ButtonImage = "VisualMode.png", // Image resource name for the button + ButtonOrder = 0, // Position of the button (lower is more to the left) + ButtonGroup = "001_visual", + UseByDefault = true)] + + public class BaseVisualMode : VisualMode + { + #region ================== Constants + + // Object picking + private const double PICK_INTERVAL = 80.0d; + private const float PICK_RANGE = 0.98f; + + // Gravity + private const float GRAVITY = -0.06f; + private const float CAMERA_FLOOR_OFFSET = 56f; // same as in doom64 + private const float CAMERA_CEILING_OFFSET = 10f; + + #endregion + + #region ================== Variables + + // Gravity + private Vector3D gravity; + private float cameraflooroffset = 56f; // same as in doom64 + private float cameraceilingoffset = 10f; + + // Object picking + private VisualPickResult target; + private double lastpicktime; + private bool locktarget; + + // This is true when a selection was made because the action is performed + // on an object that was not selected. In this case the previous selection + // is cleared and the targeted object is temporarely selected to perform + // the action on. After the action is completed, the object is deselected. + private bool singleselection; + + // We keep these to determine if we need to make a new undo level + private bool selectionchanged; + private int lastundogroup; + private VisualActionResult actionresult; + private bool undocreated; + + // List of selected objects when an action is performed + private List selectedobjects; + + #endregion + + #region ================== Properties + + public override object HighlightedObject + { + get + { + // Geometry picked? + if (target.picked is VisualGeometry) + { + VisualGeometry pickedgeo = (target.picked as VisualGeometry); + + if (pickedgeo.Sidedef != null) + return pickedgeo.Sidedef; + else if (pickedgeo.Sector != null) + return pickedgeo.Sector; + else + return null; + } + // Thing picked? + else if (target.picked is VisualThing) + { + VisualThing pickedthing = (target.picked as VisualThing); + return pickedthing.Thing; + } + else + { + return null; + } + } + } + + public IRenderer3D Renderer { get { return renderer; } } + + public bool IsSingleSelection { get { return singleselection; } } + public bool SelectionChanged { get { return selectionchanged; } set { selectionchanged |= value; } } + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + public BaseVisualMode() + { + // Initialize + this.gravity = new Vector3D(0.0f, 0.0f, 0.0f); + this.selectedobjects = new List(); + + // We have no destructor + GC.SuppressFinalize(this); + } + + // Disposer + public override void Dispose() + { + // Not already disposed? + if (!isdisposed) + { + // Clean up + + // Done + base.Dispose(); + } + } + + #endregion + + #region ================== Methods + + // This calculates brightness level + internal int CalculateBrightness(int level) + { + return renderer.CalculateBrightness(level); + } + + // This adds a selected object + internal void AddSelectedObject(IVisualEventReceiver obj) + { + selectedobjects.Add(obj); + selectionchanged = true; + } + + // This removes a selected object + internal void RemoveSelectedObject(IVisualEventReceiver obj) + { + selectedobjects.Remove(obj); + selectionchanged = true; + } + + // This is called before an action is performed + public void PreAction(int multiselectionundogroup) + { + actionresult = new VisualActionResult(); + + PickTargetUnlocked(); + + // If the action is not performed on a selected object, clear the + // current selection and make a temporary selection for the target. + if ((target.picked != null) && !target.picked.Selected && (BuilderPlug.Me.VisualModeClearSelection || (selectedobjects.Count == 0))) + { + // Single object, no selection + singleselection = true; + ClearSelection(); + undocreated = false; + } + else + { + singleselection = false; + + // Check if we should make a new undo level + // We don't want to do this if this is the same action with the same + // selection and the action wants to group the undo levels + if ((lastundogroup != multiselectionundogroup) || (lastundogroup == UndoGroup.None) || + (multiselectionundogroup == UndoGroup.None) || selectionchanged) + { + // We want to create a new undo level, but not just yet + lastundogroup = multiselectionundogroup; + undocreated = false; + } + else + { + // We don't want to make a new undo level (changes will be combined) + undocreated = true; + } + } + } + + // Called before an action is performed. This does not make an undo level + private void PreActionNoChange() + { + actionresult = new VisualActionResult(); + singleselection = false; + undocreated = false; + } + + // This is called after an action is performed + private void PostAction() + { + if (!string.IsNullOrEmpty(actionresult.displaystatus)) + General.Interface.DisplayStatus(StatusType.Action, actionresult.displaystatus); + + // Reset changed flags + foreach (KeyValuePair vs in allsectors) + { + BaseVisualSector bvs = (vs.Value as BaseVisualSector); + bvs.Floor.Changed = false; + bvs.Ceiling.Changed = false; + } + + selectionchanged = false; + + if (singleselection) + ClearSelection(); + + UpdateChangedObjects(); + ShowTargetInfo(); + } + + // This sets the result for an action + public void SetActionResult(VisualActionResult result) + { + actionresult = result; + } + + // This sets the result for an action + public void SetActionResult(string displaystatus) + { + actionresult = new VisualActionResult(); + actionresult.displaystatus = displaystatus; + } + + // This creates an undo, when only a single selection is made + // When a multi-selection is made, the undo is created by the PreAction function + public int CreateUndo(string description, int group, int grouptag) + { + if (!undocreated) + { + undocreated = true; + + if (singleselection) + return General.Map.UndoRedo.CreateUndo(description, this, group, grouptag); + else + return General.Map.UndoRedo.CreateUndo(description, this, UndoGroup.None, 0); + } + else + { + return 0; + } + } + + // This creates an undo, when only a single selection is made + // When a multi-selection is made, the undo is created by the PreAction function + public int CreateUndo(string description) + { + return CreateUndo(description, UndoGroup.None, 0); + } + + // This makes a list of the selected object + private void RebuildSelectedObjectsList() + { + // Make list of selected objects + selectedobjects = new List(); + foreach (KeyValuePair vs in allsectors) + { + BaseVisualSector bvs = (BaseVisualSector)vs.Value; + if ((bvs.Floor != null) && bvs.Floor.Selected) selectedobjects.Add(bvs.Floor); + if ((bvs.Ceiling != null) && bvs.Ceiling.Selected) selectedobjects.Add(bvs.Ceiling); + foreach (Sidedef sd in vs.Key.Sidedefs) + { + List sidedefgeos = bvs.GetSidedefGeometry(sd); + foreach (VisualGeometry sdg in sidedefgeos) + { + if (sdg.Selected) selectedobjects.Add((sdg as IVisualEventReceiver)); + } + } + } + + foreach (KeyValuePair vt in allthings) + { + BaseVisualThing bvt = (BaseVisualThing)vt.Value; + if (bvt.Selected) selectedobjects.Add(bvt); + } + } + + // This creates a visual sector + protected override VisualSector CreateVisualSector(Sector s) + { + BaseVisualSector vs = new BaseVisualSector(this, s); + return vs; + } + + // This creates a visual thing + protected override VisualThing CreateVisualThing(Thing t) + { + BaseVisualThing vt = new BaseVisualThing(this, t); + return vt.Setup() ? vt : null; + } + + // This locks the target so that it isn't changed until unlocked + public void LockTarget() + { + locktarget = true; + } + + // This unlocks the target so that is changes to the aimed geometry again + public void UnlockTarget() + { + locktarget = false; + } + + // This picks a new target, if not locked + private void PickTargetUnlocked() + { + if (!locktarget) PickTarget(); + } + + // This picks a new target + private void PickTarget() + { + // Find the object we are aiming at + Vector3D start = General.Map.VisualCamera.Position; + Vector3D delta = General.Map.VisualCamera.Target - General.Map.VisualCamera.Position; + delta = delta.GetFixedLength(General.Settings.ViewDistance * PICK_RANGE); + VisualPickResult newtarget = PickObject(start, start + delta); + + // Should we update the info on panels? + bool updateinfo = (newtarget.picked != target.picked); + + // Apply new target + target = newtarget; + + // Show target info + if (updateinfo) ShowTargetInfo(); + } + + // This shows the picked target information + public void ShowTargetInfo() + { + // Any result? + if (target.picked != null) + { + // Geometry picked? + if (target.picked is VisualGeometry) + { + VisualGeometry pickedgeo = (target.picked as VisualGeometry); + + if (pickedgeo.Sidedef != null) + General.Interface.ShowLinedefInfo(pickedgeo.Sidedef.Line); + else if (pickedgeo.Sidedef == null) + General.Interface.ShowSectorInfo(pickedgeo.Sector.Sector); + else + General.Interface.HideInfo(); + } + // Thing picked? + if (target.picked is VisualThing) + { + VisualThing pickedthing = (target.picked as VisualThing); + General.Interface.ShowThingInfo(pickedthing.Thing); + } + } + else + { + General.Interface.HideInfo(); + } + } + + // This updates the VisualSectors and VisualThings that have their Changed property set + private void UpdateChangedObjects() + { + foreach (KeyValuePair vs in allsectors) + { + BaseVisualSector bvs = (BaseVisualSector)vs.Value; + if (bvs.Changed) bvs.Rebuild(); + } + + foreach (KeyValuePair vt in allthings) + { + BaseVisualThing bvt = (BaseVisualThing)vt.Value; + if (bvt.Changed) bvt.Rebuild(); + } + } + + #endregion + + #region ================== Events + + // Help! + public override void OnHelp() + { + General.ShowHelp("e_visual.html"); + } + + // When entering this mode + public override void OnEngage() + { + base.OnEngage(); + + // Read settings + cameraflooroffset = General.Map.Config.ReadSetting("cameraflooroffset", cameraflooroffset); + cameraceilingoffset = General.Map.Config.ReadSetting("cameraceilingoffset", cameraceilingoffset); + } + + // When returning to another mode + public override void OnDisengage() + { + base.OnDisengage(); + General.Map.Map.Update(); + } + + // Processing + public override void OnProcess(double deltatime) + { + // Process things? + base.ProcessThings = (BuilderPlug.Me.ShowVisualThings != 0); + + // Setup the move multiplier depending on gravity + Vector3D movemultiplier = new Vector3D(1.0f, 1.0f, 1.0f); + if (BuilderPlug.Me.UseGravity) movemultiplier.z = 0.0f; + General.Map.VisualCamera.MoveMultiplier = movemultiplier; + + // Apply gravity? + if (BuilderPlug.Me.UseGravity && (General.Map.VisualCamera.Sector != null)) + { + // Camera below floor level? + if (General.Map.VisualCamera.Position.z <= (General.Map.VisualCamera.Sector.FloorHeight + CAMERA_FLOOR_OFFSET + 0.1f)) + { + // Stay above floor + gravity = new Vector3D(0.0f, 0.0f, 0.0f); + General.Map.VisualCamera.Position = new Vector3D(General.Map.VisualCamera.Position.x, + General.Map.VisualCamera.Position.y, + General.Map.VisualCamera.Sector.FloorHeight + cameraflooroffset); + } + else + { + // Fall down + gravity += new Vector3D(0.0f, 0.0f, (float)(GRAVITY * deltatime)); + General.Map.VisualCamera.Position += gravity; + } + + // Camera above ceiling level? + if (General.Map.VisualCamera.Position.z >= (General.Map.VisualCamera.Sector.CeilHeight - cameraceilingoffset - 0.1f)) + { + // Stay below ceiling + General.Map.VisualCamera.Position = new Vector3D(General.Map.VisualCamera.Position.x, + General.Map.VisualCamera.Position.y, + General.Map.VisualCamera.Sector.CeilHeight - cameraceilingoffset); + } + } + else + { + gravity = new Vector3D(0.0f, 0.0f, 0.0f); + } + + // Do processing + base.OnProcess(deltatime); + + // Process visible geometry + foreach (IVisualEventReceiver g in visiblegeometry) + { + g.OnProcess(deltatime); + } + + // Time to pick a new target? + if (General.stopwatch.Elapsed.TotalMilliseconds > (lastpicktime + PICK_INTERVAL)) + { + PickTargetUnlocked(); + lastpicktime = General.stopwatch.Elapsed.TotalMilliseconds; + } + + // The mouse is always in motion + MouseEventArgs args = new MouseEventArgs(General.Interface.MouseButtons, 0, 0, 0, 0); + OnMouseMove(args); + } + + // This draws a frame + public override void OnRedrawDisplay() + { + // Start drawing + if (renderer.Start()) + { + // Use fog! + renderer.SetFogMode(true); + + // Set target for highlighting + if (BuilderPlug.Me.UseHighlight) + renderer.SetHighlightedObject(target.picked); + + // Begin with geometry + renderer.StartGeometry(); + + // Render all visible sectors + foreach (VisualGeometry g in visiblegeometry) + renderer.AddSectorGeometry(g); + + if (BuilderPlug.Me.ShowVisualThings != 0) + { + // Render things in cages? + renderer.DrawThingCages = ((BuilderPlug.Me.ShowVisualThings & 2) != 0); + + // Render all visible things + foreach (VisualThing t in visiblethings) + renderer.AddThingGeometry(t); + } + + // Done rendering geometry + renderer.FinishGeometry(); + + // Render crosshair + renderer.RenderCrosshair(); + + // Present! + renderer.Finish(); + } + } + + // After resources were reloaded + protected override void ResourcesReloaded() + { + base.ResourcesReloaded(); + PickTarget(); + } + + // This usually happens when geometry is changed by undo, redo, cut or paste actions + // and uses the marks to check what needs to be reloaded. + protected override void ResourcesReloadedPartial() + { + bool sectorsmarked = false; + + if (General.Map.UndoRedo.GeometryChanged) + { + // Let the core do this (it will just dispose the sectors that were changed) + base.ResourcesReloadedPartial(); + } + else + { + // Neighbour sectors must be updated as well + foreach (Sector s in General.Map.Map.Sectors) + { + if (s.Marked) + { + sectorsmarked = true; + foreach (Sidedef sd in s.Sidedefs) + { + sd.Marked = true; + if (sd.Other != null) sd.Other.Marked = true; + } + } + } + + // Go for all sidedefs to update + foreach (Sidedef sd in General.Map.Map.Sidedefs) + { + if (sd.Marked && VisualSectorExists(sd.Sector)) + { + BaseVisualSector vs = (BaseVisualSector)GetVisualSector(sd.Sector); + VisualSidedefParts parts = vs.GetSidedefParts(sd); + parts.SetupAllParts(); + } + } + + // Go for all sectors to update + foreach (Sector s in General.Map.Map.Sectors) + { + if (s.Marked && VisualSectorExists(s)) + { + BaseVisualSector vs = (BaseVisualSector)GetVisualSector(s); + vs.Floor.Setup(); + vs.Ceiling.Setup(); + } + } + + if (!sectorsmarked) + { + // No sectors or geometry changed. So we only have + // to update things when they have changed. + foreach (KeyValuePair vt in allthings) + if (vt.Key.Marked) (vt.Value as BaseVisualThing).Rebuild(); + } + else + { + // Things depend on the sector they are in and because we can't + // easily determine which ones changed, we dispose all things + foreach (KeyValuePair vt in allthings) + vt.Value.Dispose(); + + // Apply new lists + allthings = new Dictionary(allthings.Count); + } + + // Clear visibility collections + visiblesectors.Clear(); + visibleblocks.Clear(); + visiblegeometry.Clear(); + visiblethings.Clear(); + + // Make new blockmap + if (sectorsmarked || General.Map.UndoRedo.PopulationChanged) + FillBlockMap(); + + // Visibility culling (this re-creates the needed resources) + DoCulling(); + } + + // Determine what we're aiming at now + PickTarget(); + } + + // Mouse moves + public override void OnMouseMove(MouseEventArgs e) + { + base.OnMouseMove(e); + GetTargetEventReceiver(true).OnMouseMove(e); + } + + // Undo performed + public override void OnUndoEnd() + { + base.OnUndoEnd(); + RebuildSelectedObjectsList(); + + // We can't group with this undo level anymore + lastundogroup = UndoGroup.None; + } + + // Redo performed + public override void OnRedoEnd() + { + base.OnRedoEnd(); + RebuildSelectedObjectsList(); + } + + #endregion + + #region ================== Action Assist + + // Because some actions can only be called on a single (the targeted) object because + // they show a dialog window or something, these functions help applying the result + // to all compatible selected objects. + + // Apply texture offsets + public void ApplyTextureOffsetChange(int dx, int dy) + { + Dictionary donesides = new Dictionary(selectedobjects.Count); + List objs = GetSelectedObjects(false, true, false); + foreach (IVisualEventReceiver i in objs) + { + if (i is BaseVisualGeometrySidedef) + { + if (!donesides.ContainsKey((i as BaseVisualGeometrySidedef).Sidedef)) + { + i.OnChangeTextureOffset(dx, dy); + donesides.Add((i as BaseVisualGeometrySidedef).Sidedef, 0); + } + } + } + } + + // Apply upper unpegged flag + public void ApplyUpperUnpegged(bool set) + { + List objs = GetSelectedObjects(true, true, true); + foreach (IVisualEventReceiver i in objs) + { + i.ApplyUpperUnpegged(set); + } + } + + // Apply lower unpegged flag + public void ApplyLowerUnpegged(bool set) + { + List objs = GetSelectedObjects(true, true, true); + foreach (IVisualEventReceiver i in objs) + { + i.ApplyLowerUnpegged(set); + } + } + + // Apply texture change + public void ApplySelectTexture(string texture, bool flat) + { + List objs; + + if (General.Map.Config.MixTexturesFlats) + { + // Apply on all compatible types + objs = GetSelectedObjects(true, true, false); + } + else + { + // We don't want to mix textures and flats, so apply only on the appropriate type + objs = GetSelectedObjects(flat, !flat, false); + } + + foreach (IVisualEventReceiver i in objs) + { + i.ApplyTexture(texture); + } + } + + // This returns all selected objects + internal List GetSelectedObjects(bool includesectors, bool includesidedefs, bool includethings) + { + List objs = new List(); + foreach (IVisualEventReceiver i in selectedobjects) + { + if ((i is BaseVisualGeometrySector) && includesectors) objs.Add(i); + else if ((i is BaseVisualGeometrySidedef) && includesidedefs) objs.Add(i); + else if ((i is BaseVisualThing) && includethings) objs.Add(i); + } + + // Add highlight? + if (selectedobjects.Count == 0) + { + IVisualEventReceiver i = (target.picked as IVisualEventReceiver); + if ((i is BaseVisualGeometrySector) && includesectors) objs.Add(i); + else if ((i is BaseVisualGeometrySidedef) && includesidedefs) objs.Add(i); + else if ((i is BaseVisualThing) && includethings) objs.Add(i); + } + + return objs; + } + + // This returns all selected sectors, no doubles + public List GetSelectedSectors() + { + Dictionary added = new Dictionary(); + List sectors = new List(); + foreach (IVisualEventReceiver i in selectedobjects) + { + if (i is BaseVisualGeometrySector) + { + Sector s = (i as BaseVisualGeometrySector).Sector.Sector; + if (!added.ContainsKey(s)) + { + sectors.Add(s); + added.Add(s, 0); + } + } + } + + // Add highlight? + if ((selectedobjects.Count == 0) && (target.picked is BaseVisualGeometrySector)) + { + Sector s = (target.picked as BaseVisualGeometrySector).Sector.Sector; + if (!added.ContainsKey(s)) + sectors.Add(s); + } + + return sectors; + } + + // This returns all selected linedefs, no doubles + public List GetSelectedLinedefs() + { + Dictionary added = new Dictionary(); + List linedefs = new List(); + foreach (IVisualEventReceiver i in selectedobjects) + { + if (i is BaseVisualGeometrySidedef) + { + Linedef l = (i as BaseVisualGeometrySidedef).Sidedef.Line; + if (!added.ContainsKey(l)) + { + linedefs.Add(l); + added.Add(l, 0); + } + } + } + + // Add highlight? + if ((selectedobjects.Count == 0) && (target.picked is BaseVisualGeometrySidedef)) + { + Linedef l = (target.picked as BaseVisualGeometrySidedef).Sidedef.Line; + if (!added.ContainsKey(l)) + linedefs.Add(l); + } + + return linedefs; + } + + // This returns all selected sidedefs, no doubles + public List GetSelectedSidedefs() + { + Dictionary added = new Dictionary(); + List sidedefs = new List(); + foreach (IVisualEventReceiver i in selectedobjects) + { + if (i is BaseVisualGeometrySidedef) + { + Sidedef sd = (i as BaseVisualGeometrySidedef).Sidedef; + if (!added.ContainsKey(sd)) + { + sidedefs.Add(sd); + added.Add(sd, 0); + } + } + } + + // Add highlight? + if ((selectedobjects.Count == 0) && (target.picked is BaseVisualGeometrySidedef)) + { + Sidedef sd = (target.picked as BaseVisualGeometrySidedef).Sidedef; + if (!added.ContainsKey(sd)) + sidedefs.Add(sd); + } + + return sidedefs; + } + + // This returns all selected things, no doubles + public List GetSelectedThings() + { + Dictionary added = new Dictionary(); + List things = new List(); + foreach (IVisualEventReceiver i in selectedobjects) + { + if (i is BaseVisualThing) + { + Thing t = (i as BaseVisualThing).Thing; + if (!added.ContainsKey(t)) + { + things.Add(t); + added.Add(t, 0); + } + } + } + + // Add highlight? + if ((selectedobjects.Count == 0) && (target.picked is BaseVisualThing)) + { + Thing t = (target.picked as BaseVisualThing).Thing; + if (!added.ContainsKey(t)) + things.Add(t); + } + + return things; + } + + // This returns the IVisualEventReceiver on which the action must be performed + private IVisualEventReceiver GetTargetEventReceiver(bool targetonly) + { + if (target.picked != null) + { + if (singleselection || target.picked.Selected || targetonly) + { + return (IVisualEventReceiver)target.picked; + } + else if (selectedobjects.Count > 0) + { + return selectedobjects[0]; + } + else + { + return (IVisualEventReceiver)target.picked; + } + } + else + { + return new NullVisualEventReceiver(); + } + } + + #endregion + + #region ================== Actions + + [BeginAction("clearselection", BaseAction = true)] + public void ClearSelection() + { + selectedobjects = new List(); + + foreach (KeyValuePair vs in allsectors) + { + BaseVisualSector bvs = (BaseVisualSector)vs.Value; + if (bvs.Floor != null) bvs.Floor.Selected = false; + if (bvs.Ceiling != null) bvs.Ceiling.Selected = false; + foreach (Sidedef sd in vs.Key.Sidedefs) + { + List sidedefgeos = bvs.GetSidedefGeometry(sd); + foreach (VisualGeometry sdg in sidedefgeos) + { + sdg.Selected = false; + } + } + } + + foreach (KeyValuePair vt in allthings) + { + BaseVisualThing bvt = (BaseVisualThing)vt.Value; + bvt.Selected = false; + } + } + + [BeginAction("visualselect", BaseAction = true)] + public void BeginSelect() + { + PreActionNoChange(); + PickTargetUnlocked(); + GetTargetEventReceiver(true).OnSelectBegin(); + PostAction(); + } + + [EndAction("visualselect", BaseAction = true)] + public void EndSelect() + { + //PreActionNoChange(); + GetTargetEventReceiver(true).OnSelectEnd(); + Renderer.ShowSelection = true; + Renderer.ShowHighlight = true; + PostAction(); + } + + [BeginAction("visualedit", BaseAction = true)] + public void BeginEdit() + { + PreAction(UndoGroup.None); + GetTargetEventReceiver(false).OnEditBegin(); + PostAction(); + } + + [EndAction("visualedit", BaseAction = true)] + public void EndEdit() + { + PreActionNoChange(); + GetTargetEventReceiver(false).OnEditEnd(); + PostAction(); + } + + [BeginAction("raisesector8")] + public void RaiseSector8() + { + PreAction(UndoGroup.SectorHeightChange); + List objs = GetSelectedObjects(true, true, true); + foreach (IVisualEventReceiver i in objs) i.OnChangeTargetHeight(8); + PostAction(); + } + + [BeginAction("lowersector8")] + public void LowerSector8() + { + PreAction(UndoGroup.SectorHeightChange); + List objs = GetSelectedObjects(true, true, true); + foreach (IVisualEventReceiver i in objs) i.OnChangeTargetHeight(-8); + PostAction(); + } + + [BeginAction("raisesector1")] + public void RaiseSector1() + { + PreAction(UndoGroup.SectorHeightChange); + List objs = GetSelectedObjects(true, true, true); + foreach (IVisualEventReceiver i in objs) i.OnChangeTargetHeight(1); + PostAction(); + } + + [BeginAction("lowersector1")] + public void LowerSector1() + { + PreAction(UndoGroup.SectorHeightChange); + List objs = GetSelectedObjects(true, true, true); + foreach (IVisualEventReceiver i in objs) i.OnChangeTargetHeight(-1); + PostAction(); + } + + [BeginAction("showvisualthings")] + public void ShowVisualThings() + { + BuilderPlug.Me.ShowVisualThings++; + if (BuilderPlug.Me.ShowVisualThings > 2) BuilderPlug.Me.ShowVisualThings = 0; + } + + [BeginAction("movetextureleft")] + public void MoveTextureLeft1() + { + PreAction(UndoGroup.TextureOffsetChange); + List objs = GetSelectedObjects(true, true, true); + foreach (IVisualEventReceiver i in objs) i.OnChangeTextureOffset(-1, 0); + PostAction(); + } + + [BeginAction("movetextureright")] + public void MoveTextureRight1() + { + PreAction(UndoGroup.TextureOffsetChange); + List objs = GetSelectedObjects(true, true, true); + foreach (IVisualEventReceiver i in objs) i.OnChangeTextureOffset(1, 0); + PostAction(); + } + + [BeginAction("movetextureup")] + public void MoveTextureUp1() + { + PreAction(UndoGroup.TextureOffsetChange); + List objs = GetSelectedObjects(true, true, true); + foreach (IVisualEventReceiver i in objs) i.OnChangeTextureOffset(0, -1); + PostAction(); + } + + [BeginAction("movetexturedown")] + public void MoveTextureDown1() + { + PreAction(UndoGroup.TextureOffsetChange); + List objs = GetSelectedObjects(true, true, true); + foreach (IVisualEventReceiver i in objs) i.OnChangeTextureOffset(0, 1); + PostAction(); + } + + [BeginAction("movetextureleft8")] + public void MoveTextureLeft8() + { + PreAction(UndoGroup.TextureOffsetChange); + List objs = GetSelectedObjects(true, true, true); + foreach (IVisualEventReceiver i in objs) i.OnChangeTextureOffset(-8, 0); + PostAction(); + } + + [BeginAction("movetextureright8")] + public void MoveTextureRight8() + { + PreAction(UndoGroup.TextureOffsetChange); + List objs = GetSelectedObjects(true, true, true); + foreach (IVisualEventReceiver i in objs) i.OnChangeTextureOffset(8, 0); + PostAction(); + } + + [BeginAction("movetextureup8")] + public void MoveTextureUp8() + { + PreAction(UndoGroup.TextureOffsetChange); + List objs = GetSelectedObjects(true, true, true); + foreach (IVisualEventReceiver i in objs) i.OnChangeTextureOffset(0, -8); + PostAction(); + } + + [BeginAction("movetexturedown8")] + public void MoveTextureDown8() + { + PreAction(UndoGroup.TextureOffsetChange); + List objs = GetSelectedObjects(true, true, true); + foreach (IVisualEventReceiver i in objs) i.OnChangeTextureOffset(0, 8); + PostAction(); + } + + [BeginAction("textureselect")] + public void TextureSelect() + { + PreAction(UndoGroup.None); + renderer.SetCrosshairBusy(true); + General.Interface.RedrawDisplay(); + GetTargetEventReceiver(false).OnSelectTexture(); + UpdateChangedObjects(); + renderer.SetCrosshairBusy(false); + PostAction(); + } + + [BeginAction("texturecopy")] + public void TextureCopy() + { + PreActionNoChange(); + GetTargetEventReceiver(false).OnCopyTexture(); + PostAction(); + } + + [BeginAction("texturepaste")] + public void TexturePaste() + { + PreAction(UndoGroup.None); + List objs = GetSelectedObjects(true, true, true); + foreach (IVisualEventReceiver i in objs) i.OnPasteTexture(); + PostAction(); + } + + // [villsa start] + [BeginAction("lightcopy")] + public void LightCopy() + { + PreActionNoChange(); + GetTargetEventReceiver(false).OnCopyLight(); + PostAction(); + } + + [BeginAction("lightpaste")] + public void LightPaste() + { + PreAction(UndoGroup.None); + List objs = GetSelectedObjects(true, true, true); + foreach (IVisualEventReceiver i in objs) i.OnPasteLight(); + PostAction(); + } + // [villsa end] + + //mxd + [BeginAction("visualautoalign")] + public void TextureAutoAlign() + { + PreAction(UndoGroup.None); + renderer.SetCrosshairBusy(true); + General.Interface.RedrawDisplay(); + GetTargetEventReceiver(false).OnTextureAlign(true, true); + UpdateChangedObjects(); + renderer.SetCrosshairBusy(false); + PostAction(); + } + [BeginAction("visualautoalignx")] + public void TextureAutoAlignX() + { + PreAction(UndoGroup.None); + renderer.SetCrosshairBusy(true); + General.Interface.RedrawDisplay(); + GetTargetEventReceiver(false).OnTextureAlign(true, false); + UpdateChangedObjects(); + renderer.SetCrosshairBusy(false); + PostAction(); + } + + [BeginAction("visualautoaligny")] + public void TextureAutoAlignY() + { + PreAction(UndoGroup.None); + renderer.SetCrosshairBusy(true); + General.Interface.RedrawDisplay(); + GetTargetEventReceiver(false).OnTextureAlign(false, true); + UpdateChangedObjects(); + renderer.SetCrosshairBusy(false); + PostAction(); + } + + [BeginAction("toggleupperunpegged")] + public void ToggleUpperUnpegged() + { + PreAction(UndoGroup.None); + GetTargetEventReceiver(false).OnToggleUpperUnpegged(); + PostAction(); + } + + [BeginAction("togglelowerunpegged")] + public void ToggleLowerUnpegged() + { + PreAction(UndoGroup.None); + GetTargetEventReceiver(false).OnToggleLowerUnpegged(); + PostAction(); + } + + [BeginAction("togglegravity")] + public void ToggleGravity() + { + BuilderPlug.Me.UseGravity = !BuilderPlug.Me.UseGravity; + string onoff = BuilderPlug.Me.UseGravity ? "ON" : "OFF"; + General.Interface.DisplayStatus(StatusType.Action, "Gravity is now " + onoff + "."); + } + + [BeginAction("togglelightonly")] + public void ToggleLightOnly() + { + renderer.ShowLightOnly = !renderer.ShowLightOnly; + string onoff = renderer.ShowLightOnly ? "ON" : "OFF"; + General.Interface.DisplayStatus(StatusType.Action, "Lighting only is now " + onoff + "."); + } + + [BeginAction("togglehighlight")] + public void ToggleHighlight() + { + BuilderPlug.Me.UseHighlight = !BuilderPlug.Me.UseHighlight; + string onoff = BuilderPlug.Me.UseHighlight ? "ON" : "OFF"; + General.Interface.DisplayStatus(StatusType.Action, "Highlight is now " + onoff + "."); + } + + [BeginAction("resettexture")] + public void ResetTexture() + { + PreAction(UndoGroup.None); + List objs = GetSelectedObjects(true, true, true); + foreach (IVisualEventReceiver i in objs) i.OnResetTextureOffset(); + PostAction(); + } + + [BeginAction("floodfilltextures")] + public void FloodfillTextures() + { + PreAction(UndoGroup.None); + GetTargetEventReceiver(false).OnTextureFloodfill(); + PostAction(); + } + + [BeginAction("texturecopyoffsets")] + public void TextureCopyOffsets() + { + PreActionNoChange(); + GetTargetEventReceiver(false).OnCopyTextureOffsets(); + PostAction(); + } + + [BeginAction("texturepasteoffsets")] + public void TexturePasteOffsets() + { + PreAction(UndoGroup.None); + List objs = GetSelectedObjects(true, true, true); + foreach (IVisualEventReceiver i in objs) i.OnPasteTextureOffsets(); + PostAction(); + } + + [BeginAction("copyproperties")] + public void CopyProperties() + { + PreActionNoChange(); + GetTargetEventReceiver(false).OnCopyProperties(); + PostAction(); + } + + [BeginAction("pasteproperties")] + public void PasteProperties() + { + PreAction(UndoGroup.None); + List objs = GetSelectedObjects(true, true, true); + foreach (IVisualEventReceiver i in objs) i.OnPasteProperties(); + PostAction(); + } + + [BeginAction("insertitem", BaseAction = true)] + public void Insert() + { + PreAction(UndoGroup.None); + List objs = GetSelectedObjects(true, true, true); + foreach (IVisualEventReceiver i in objs) i.OnInsert(); + PostAction(); + } + + [BeginAction("deleteitem", BaseAction = true)] + public void Delete() + { + PreAction(UndoGroup.None); + List objs = GetSelectedObjects(true, true, true); + foreach (IVisualEventReceiver i in objs) i.OnDelete(); + PostAction(); + } + + #endregion + } +} diff --git a/Source/Plugins/BuilderModes/VisualModes/BaseVisualSector.cs b/Source/Plugins/BuilderModes/VisualModes/BaseVisualSector.cs new file mode 100644 index 0000000..944a238 --- /dev/null +++ b/Source/Plugins/BuilderModes/VisualModes/BaseVisualSector.cs @@ -0,0 +1,209 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using CodeImp.DoomBuilder.Windows; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Geometry; +using CodeImp.DoomBuilder.Editing; +using CodeImp.DoomBuilder.VisualModes; + +#endregion + +namespace CodeImp.DoomBuilder.BuilderModes +{ + internal class BaseVisualSector : VisualSector + { + #region ================== Constants + + #endregion + + #region ================== Variables + + protected BaseVisualMode mode; + + protected VisualFloor floor; + protected VisualCeiling ceiling; + protected Dictionary sides; + + // If this is set to true, the sector will be rebuilt after the action is performed. + protected bool changed; + + #endregion + + #region ================== Properties + + public VisualFloor Floor { get { return floor; } } + public VisualCeiling Ceiling { get { return ceiling; } } + public bool Changed { get { return changed; } set { changed |= value; } } + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + public BaseVisualSector(BaseVisualMode mode, Sector s) : base(s) + { + this.mode = mode; + + // Initialize + Rebuild(); + + // We have no destructor + GC.SuppressFinalize(this); + } + + // Disposer + public override void Dispose() + { + // Not already disposed? + if (!IsDisposed) + { + // Clean up + sides = null; + floor = null; + ceiling = null; + + // Dispose base + base.Dispose(); + } + } + + #endregion + + #region ================== Methods + + // Thisvirtuals the secotr and neightbours if needed + public void UpdateSectorGeometry(bool includeneighbours) + { + // Rebuild sector + this.Changed = true; + + // Go for all things in this sector + foreach (Thing t in General.Map.Map.Things) + { + if (t.Sector == this.Sector) + { + if (mode.VisualThingExists(t)) + { + // Update thing + BaseVisualThing vt = (mode.GetVisualThing(t) as BaseVisualThing); + vt.Changed = true; + } + } + } + + if (includeneighbours) + { + // Also rebuild surrounding sectors, because outside sidedefs may need to be adjusted + foreach (Sidedef sd in this.Sector.Sidedefs) + { + if (sd.Other != null) + { + if (mode.VisualSectorExists(sd.Other.Sector)) + { + BaseVisualSector bvs = (BaseVisualSector)mode.GetVisualSector(sd.Other.Sector); + bvs.Changed = true; + } + } + } + } + } + + // This (re)builds the visual sector, calculating all geometry from scratch + public void Rebuild() + { + // Forget old geometry + base.ClearGeometry(); + + // Create floor + if (floor == null) floor = new VisualFloor(mode, this); + floor.Setup(); + base.AddGeometry(floor); + + // Create ceiling + if (ceiling == null) ceiling = new VisualCeiling(mode, this); + ceiling.Setup(); + base.AddGeometry(ceiling); + + // Go for all sidedefs + Dictionary oldsides = sides ?? new Dictionary(1); + sides = new Dictionary(base.Sector.Sidedefs.Count); + foreach (Sidedef sd in base.Sector.Sidedefs) + { + // VisualSidedef already exists? + VisualSidedefParts parts = oldsides.ContainsKey(sd) ? oldsides[sd] : new VisualSidedefParts(); + + // Doublesided or singlesided? + if (sd.Other != null) + { + // Create upper part + VisualUpper vu = parts.upper ?? new VisualUpper(mode, this, sd); + vu.Setup(); + base.AddGeometry(vu); + + // Create lower part + VisualLower vl = parts.lower ?? new VisualLower(mode, this, sd); + vl.Setup(); + base.AddGeometry(vl); + + // Create middle part + VisualMiddleDouble vm = parts.middledouble ?? new VisualMiddleDouble(mode, this, sd); + vm.Setup(); + base.AddGeometry(vm); + + // Store + sides.Add(sd, new VisualSidedefParts(vu, vl, vm)); + } + else + { + // Create middle part + VisualMiddleSingle vm = parts.middlesingle ?? new VisualMiddleSingle(mode, this, sd); + vm.Setup(); + base.AddGeometry(vm); + + // Store + sides.Add(sd, new VisualSidedefParts(vm)); + } + } + + // Done + changed = false; + } + + // This returns the visual sidedef parts for a given sidedef + public VisualSidedefParts GetSidedefParts(Sidedef sd) + { + if (sides.ContainsKey(sd)) + return sides[sd]; + else + return new VisualSidedefParts(); + } + + #endregion + } +} diff --git a/Source/Plugins/BuilderModes/VisualModes/BaseVisualThing.cs b/Source/Plugins/BuilderModes/VisualModes/BaseVisualThing.cs new file mode 100644 index 0000000..3816f77 --- /dev/null +++ b/Source/Plugins/BuilderModes/VisualModes/BaseVisualThing.cs @@ -0,0 +1,496 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using CodeImp.DoomBuilder.Windows; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Geometry; +using CodeImp.DoomBuilder.Editing; +using CodeImp.DoomBuilder.VisualModes; +using CodeImp.DoomBuilder.Config; +using CodeImp.DoomBuilder.Data; + +#endregion + +namespace CodeImp.DoomBuilder.BuilderModes +{ + internal class BaseVisualThing : VisualThing, IVisualEventReceiver + { + #region ================== Constants + + #endregion + + #region ================== Variables + + protected BaseVisualMode mode; + + private ThingTypeInfo info; + private bool isloaded; + private ImageData sprite; + private float cageradius2; + private Vector2D pos2d; + private Vector3D boxp1; + private Vector3D boxp2; + + // Undo/redo + private int undoticket; + + // If this is set to true, the thing will be rebuilt after the action is performed. + protected bool changed; + + #endregion + + #region ================== Properties + + public bool Changed { get { return changed; } set { changed |= value; } } + + #endregion + + #region ================== Constructor / Setup + + // Constructor + public BaseVisualThing(BaseVisualMode mode, Thing t) : base(t) + { + this.mode = mode; + + Rebuild(); + + // We have no destructor + GC.SuppressFinalize(this); + } + + // This builds the thing geometry. Returns false when nothing was created. + public virtual bool Setup() + { + int sectorcolor = 0; + + // Must have a width and height! + if ((info.Radius < 0.1f) || (info.Height < 0.1f)) return false; + + // Find the sector in which the thing resides + Thing.DetermineSector(mode.BlockMap); + + if (sprite != null) + { + if (Thing.Sector != null) + { + // Use sector brightness for color shading + //byte brightness = (byte)General.Clamp(Thing.Sector.Brightness, 0, 255); + //sectorcolor = new PixelColor(255, brightness, brightness, brightness); + + // villsa 9/11/11 (builder64) cameras/triggers are rendered full bright + if (Thing.Type == 0 || Thing.Type == 89) + sectorcolor = -1; + else + sectorcolor = Thing.Sector.ThingColor.GetColor(); + } + + // villsa 9/11/11 (builder64) render camera/trigger icon + if (Thing.Type == 0 || Thing.Type == 89) + { + if (Thing.Type == 89) + { + base.Texture = General.Map.Data.ThingTrigger; + } + else + { + base.Texture = General.Map.Data.ThingCamera; + } + + // Determine sprite size + float radius = Math.Min(info.Radius, info.Height / 2f); + float height = Math.Min(info.Radius * 2f, info.Height); + + // Make vertices + WorldVertex[] verts = new WorldVertex[6]; + verts[0] = new WorldVertex(-radius, 0.0f, 0.0f, sectorcolor, 0.0f, 1.0f); + verts[1] = new WorldVertex(-radius, 0.0f, height, sectorcolor, 0.0f, 0.0f); + verts[2] = new WorldVertex(+radius, 0.0f, height, sectorcolor, 1.0f, 0.0f); + verts[3] = verts[0]; + verts[4] = verts[2]; + verts[5] = new WorldVertex(+radius, 0.0f, 0.0f, sectorcolor, 1.0f, 1.0f); + SetVertices(verts); + } + else + { + // Check if the texture is loaded + sprite.LoadImage(); + isloaded = sprite.IsImageLoaded; + if (isloaded) + { + float offsetx = 0.0f; + float offsety = 0.0f; + + base.Texture = sprite; + + // Determine sprite size and offset + float radius = sprite.ScaledWidth * 0.5f; + float height = sprite.ScaledHeight; + if (sprite is SpriteImage) + { + offsetx = (sprite as SpriteImage).OffsetX - radius; + offsety = (sprite as SpriteImage).OffsetY - height; + } + + // Make vertices + WorldVertex[] verts = new WorldVertex[6]; + verts[0] = new WorldVertex(-radius + offsetx, 0.0f, 0.0f + offsety, sectorcolor, 0.0f, 1.0f); + verts[1] = new WorldVertex(-radius + offsetx, 0.0f, height + offsety, sectorcolor, 0.0f, 0.0f); + verts[2] = new WorldVertex(+radius + offsetx, 0.0f, height + offsety, sectorcolor, 1.0f, 0.0f); + verts[3] = verts[0]; + verts[4] = verts[2]; + verts[5] = new WorldVertex(+radius + offsetx, 0.0f, 0.0f + offsety, sectorcolor, 1.0f, 1.0f); + SetVertices(verts); + } + else + { + base.Texture = General.Map.Data.Hourglass3D; + + // Determine sprite size + float radius = Math.Min(info.Radius, info.Height / 2f); + float height = Math.Min(info.Radius * 2f, info.Height); + + // Make vertices + WorldVertex[] verts = new WorldVertex[6]; + verts[0] = new WorldVertex(-radius, 0.0f, 0.0f, sectorcolor, 0.0f, 1.0f); + verts[1] = new WorldVertex(-radius, 0.0f, height, sectorcolor, 0.0f, 0.0f); + verts[2] = new WorldVertex(+radius, 0.0f, height, sectorcolor, 1.0f, 0.0f); + verts[3] = verts[0]; + verts[4] = verts[2]; + verts[5] = new WorldVertex(+radius, 0.0f, 0.0f, sectorcolor, 1.0f, 1.0f); + SetVertices(verts); + } + } + } + + // Determine position + Vector3D pos = Thing.Position; + if (info.AbsoluteZ) + { + // Absolute Z position + pos.z = Thing.Position.z; + } + else if (info.Hangs) + { + // Hang from ceiling + if (Thing.Sector != null) pos.z = Thing.Sector.CeilHeight - info.Height; + if (Thing.Position.z > 0) pos.z -= Thing.Position.z; + + // Check if below floor + if ((Thing.Sector != null) && (pos.z < Thing.Sector.FloorHeight)) + { + // Put thing on the floor + pos.z = Thing.Sector.FloorHeight; + } + } + else + { + // Stand on floor + if (Thing.Sector != null) pos.z = Thing.Sector.FloorHeight; + if (Thing.Position.z > 0) pos.z += Thing.Position.z; + + // Check if above ceiling + if ((Thing.Sector != null) && ((pos.z + info.Height) > Thing.Sector.CeilHeight)) + { + // Put thing against ceiling + pos.z = Thing.Sector.CeilHeight - info.Height; + } + } + + // Apply settings + SetPosition(pos); + SetCageSize(info.Radius, info.Height); + SetCageColor(Thing.Color); + + // Keep info for object picking + cageradius2 = info.Radius * Angle2D.SQRT2; + cageradius2 = cageradius2 * cageradius2; + pos2d = pos; + boxp1 = new Vector3D(pos.x - info.Radius, pos.y - info.Radius, pos.z); + boxp2 = new Vector3D(pos.x + info.Radius, pos.y + info.Radius, pos.z + info.Height); + + // Done + changed = false; + return true; + } + + // Disposing + public override void Dispose() + { + if (!IsDisposed) + { + if (sprite != null) + { + sprite.RemoveReference(); + sprite = null; + } + } + + base.Dispose(); + } + + #endregion + + #region ================== Methods + + // This forces to rebuild the whole thing + public void Rebuild() + { + // Find thing information + info = General.Map.Data.GetThingInfo(Thing.Type); + + // Find sprite texture + if (info.Sprite.Length > 0) + { + sprite = General.Map.Data.GetSpriteImage(info.Sprite); + if (sprite != null) sprite.AddReference(); + } + + // Setup visual thing + Setup(); + } + + // This updates the thing when needed + public override void Update() + { + if (!isloaded) + { + // Rebuild sprite geometry when sprite is loaded + if (sprite.IsImageLoaded) + { + Setup(); + } + } + + // Let the base update + base.Update(); + } + + // This performs a fast test in object picking + public override bool PickFastReject(Vector3D from, Vector3D to, Vector3D dir) + { + float distance2 = Line2D.GetDistanceToLineSq(from, to, pos2d, false); + return (distance2 <= cageradius2); + } + + // This performs an accurate test for object picking + public override bool PickAccurate(Vector3D from, Vector3D to, Vector3D dir, ref float u_ray) + { + Vector3D delta = to - from; + float tfar = float.MaxValue; + float tnear = float.MinValue; + + // Ray-Box intersection code + // See http://www.masm32.com/board/index.php?topic=9941.0 + + // Check X slab + if (delta.x == 0.0f) + { + if (from.x > boxp2.x || from.x < boxp1.x) + { + // Ray is parallel to the planes & outside slab + return false; + } + } + else + { + float tmp = 1.0f / delta.x; + float t1 = (boxp1.x - from.x) * tmp; + float t2 = (boxp2.x - from.x) * tmp; + if (t1 > t2) General.Swap(ref t1, ref t2); + if (t1 > tnear) tnear = t1; + if (t2 < tfar) tfar = t2; + if (tnear > tfar || tfar < 0.0f) + { + // Ray missed box or box is behind ray + return false; + } + } + + // Check Y slab + if (delta.y == 0.0f) + { + if (from.y > boxp2.y || from.y < boxp1.y) + { + // Ray is parallel to the planes & outside slab + return false; + } + } + else + { + float tmp = 1.0f / delta.y; + float t1 = (boxp1.y - from.y) * tmp; + float t2 = (boxp2.y - from.y) * tmp; + if (t1 > t2) General.Swap(ref t1, ref t2); + if (t1 > tnear) tnear = t1; + if (t2 < tfar) tfar = t2; + if (tnear > tfar || tfar < 0.0f) + { + // Ray missed box or box is behind ray + return false; + } + } + + // Check Z slab + if (delta.z == 0.0f) + { + if (from.z > boxp2.z || from.z < boxp1.z) + { + // Ray is parallel to the planes & outside slab + return false; + } + } + else + { + float tmp = 1.0f / delta.z; + float t1 = (boxp1.z - from.z) * tmp; + float t2 = (boxp2.z - from.z) * tmp; + if (t1 > t2) General.Swap(ref t1, ref t2); + if (t1 > tnear) tnear = t1; + if (t2 < tfar) tfar = t2; + if (tnear > tfar || tfar < 0.0f) + { + // Ray missed box or box is behind ray + return false; + } + } + + // Set interpolation point + u_ray = (tnear > 0.0f) ? tnear : tfar; + return true; + } + + #endregion + + #region ================== Events + + // Unused + public virtual void OnSelectBegin() { } + public virtual void OnEditBegin() { } + public virtual void OnMouseMove(MouseEventArgs e) { } + public virtual void OnChangeTargetBrightness(bool up) { } + public virtual void OnChangeTextureOffset(int horizontal, int vertical) { } + public virtual void OnSelectTexture() { } + public virtual void OnCopyTexture() { } + public virtual void OnPasteTexture() { } + public virtual void OnCopyLight() { } // villsa + public virtual void OnPasteLight() { } // villsa + public virtual void OnCopyTextureOffsets() { } + public virtual void OnPasteTextureOffsets() { } + public virtual void OnTextureAlign(bool alignx, bool aligny) { } + public virtual void OnToggleUpperUnpegged() { } + public virtual void OnToggleLowerUnpegged() { } + public virtual void OnResetTextureOffset() { } + public virtual void OnProcess(double deltatime) { } + public virtual void OnTextureFloodfill() { } + public virtual void OnInsert() { } + public virtual void OnDelete() { } + public virtual void ApplyTexture(string texture) { } + public virtual void ApplyUpperUnpegged(bool set) { } + public virtual void ApplyLowerUnpegged(bool set) { } + + // Return texture name + public virtual string GetTextureName() { return ""; } + + // Select or deselect + public virtual void OnSelectEnd() + { + if (this.selected) + { + this.selected = false; + mode.RemoveSelectedObject(this); + } + else + { + this.selected = true; + mode.AddSelectedObject(this); + } + } + + // Copy properties + public virtual void OnCopyProperties() + { + BuilderPlug.Me.CopiedThingProps = new ThingProperties(Thing); + mode.SetActionResult("Copied thing properties."); + } + + // Paste properties + public virtual void OnPasteProperties() + { + if (BuilderPlug.Me.CopiedThingProps != null) + { + mode.CreateUndo("Paste thing properties"); + mode.SetActionResult("Pasted thing properties."); + BuilderPlug.Me.CopiedThingProps.Apply(Thing); + Thing.UpdateConfiguration(); + this.Rebuild(); + mode.ShowTargetInfo(); + } + } + + // Edit button released + public virtual void OnEditEnd() + { + // Not using any modifier buttons + if (!General.Interface.ShiftState && !General.Interface.CtrlState && !General.Interface.AltState) + { + if (General.Interface.IsActiveWindow) + { + List things = mode.GetSelectedThings(); + DialogResult result = General.Interface.ShowEditThings(things); + if (result == DialogResult.OK) + { + foreach (Thing t in things) + { + VisualThing vt = mode.GetVisualThing(t); + if (vt != null) + (vt as BaseVisualThing).Changed = true; + } + } + } + } + } + + // Raise/lower thing + public virtual void OnChangeTargetHeight(int amount) + { + if (General.Map.FormatInterface.HasThingHeight) + { + if ((General.Map.UndoRedo.NextUndo == null) || (General.Map.UndoRedo.NextUndo.TicketID != undoticket)) + undoticket = mode.CreateUndo("Change thing height"); + + Thing.Move(Thing.Position + new Vector3D(0.0f, 0.0f, (float)amount)); + + mode.SetActionResult("Changed thing height to " + Thing.Position.z + "."); + + this.Changed = true; + } + } + + #endregion + } +} diff --git a/Source/Plugins/BuilderModes/VisualModes/IVisualEventReceiver.cs b/Source/Plugins/BuilderModes/VisualModes/IVisualEventReceiver.cs new file mode 100644 index 0000000..6ef3934 --- /dev/null +++ b/Source/Plugins/BuilderModes/VisualModes/IVisualEventReceiver.cs @@ -0,0 +1,76 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using CodeImp.DoomBuilder.Windows; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Geometry; +using CodeImp.DoomBuilder.Editing; +using CodeImp.DoomBuilder.VisualModes; + +#endregion + +namespace CodeImp.DoomBuilder.BuilderModes +{ + internal interface IVisualEventReceiver + { + // The events that must be handled + void OnSelectBegin(); + void OnSelectEnd(); + void OnEditBegin(); + void OnEditEnd(); + void OnMouseMove(MouseEventArgs e); + void OnChangeTargetHeight(int amount); + void OnChangeTargetBrightness(bool up); + void OnChangeTextureOffset(int horizontal, int vertical); + void OnResetTextureOffset(); + void OnSelectTexture(); + void OnCopyTexture(); + void OnPasteTexture(); + void OnCopyLight(); + void OnPasteLight(); + void OnCopyTextureOffsets(); + void OnPasteTextureOffsets(); + void OnCopyProperties(); + void OnPasteProperties(); + void OnTextureAlign(bool alignx, bool aligny); + void OnTextureFloodfill(); + void OnToggleUpperUnpegged(); + void OnToggleLowerUnpegged(); + void OnProcess(double deltatime); + void OnInsert(); + void OnDelete(); + + // Assist functions + void ApplyTexture(string texture); + void ApplyUpperUnpegged(bool set); + void ApplyLowerUnpegged(bool set); + + // Other methods + string GetTextureName(); + } +} diff --git a/Source/Plugins/BuilderModes/VisualModes/NullVisualEventReceiver.cs b/Source/Plugins/BuilderModes/VisualModes/NullVisualEventReceiver.cs new file mode 100644 index 0000000..0f7a90c --- /dev/null +++ b/Source/Plugins/BuilderModes/VisualModes/NullVisualEventReceiver.cs @@ -0,0 +1,165 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using CodeImp.DoomBuilder.Windows; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Geometry; +using CodeImp.DoomBuilder.Editing; +using CodeImp.DoomBuilder.VisualModes; + +#endregion + +namespace CodeImp.DoomBuilder.BuilderModes +{ + // This doesn't do jack shit. + internal class NullVisualEventReceiver : IVisualEventReceiver + { + public NullVisualEventReceiver() + { + } + + public void OnSelectBegin() + { + } + + public void OnSelectEnd() + { + } + + public void OnEditBegin() + { + } + + public void OnEditEnd() + { + } + + public void OnMouseMove(MouseEventArgs e) + { + } + + public void OnChangeTargetHeight(int amount) + { + } + + public void OnChangeTargetBrightness(bool up) + { + } + + public void OnChangeTextureOffset(int horizontal, int vertical) + { + } + + public void OnResetTextureOffset() + { + } + + public void OnSelectTexture() + { + } + + public void OnCopyTexture() + { + } + + public void OnPasteTexture() + { + } + + // villsa + public void OnCopyLight() + { + } + + // villsa + public void OnPasteLight() + { + } + + public void OnCopyTextureOffsets() + { + } + + public void OnPasteTextureOffsets() + { + } + + public void OnCopyProperties() + { + } + + public void OnPasteProperties() + { + } + + public void OnTextureAlign(bool alignx, bool aligny) + { + } + + public void OnTextureFloodfill() + { + } + + public void OnToggleUpperUnpegged() + { + } + + public void OnToggleLowerUnpegged() + { + } + + public void OnProcess(double deltatime) + { + } + + public void OnInsert() + { + } + + public void OnDelete() + { + } + + public void ApplyTexture(string texture) + { + } + + public void ApplyUpperUnpegged(bool set) + { + } + + public void ApplyLowerUnpegged(bool set) + { + } + + public string GetTextureName() + { + return ""; + } + } +} diff --git a/Source/Plugins/BuilderModes/VisualModes/VisualActionResult.cs b/Source/Plugins/BuilderModes/VisualModes/VisualActionResult.cs new file mode 100644 index 0000000..27f50a9 --- /dev/null +++ b/Source/Plugins/BuilderModes/VisualModes/VisualActionResult.cs @@ -0,0 +1,46 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using CodeImp.DoomBuilder.Windows; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Geometry; +using CodeImp.DoomBuilder.Editing; +using CodeImp.DoomBuilder.VisualModes; + +#endregion + +namespace CodeImp.DoomBuilder.BuilderModes +{ + public struct VisualActionResult + { + /// + /// Status description to show after action hasbeen performed. Set to null to show no message. + /// + public string displaystatus; + } +} diff --git a/Source/Plugins/BuilderModes/VisualModes/VisualCeiling.cs b/Source/Plugins/BuilderModes/VisualModes/VisualCeiling.cs new file mode 100644 index 0000000..c5af121 --- /dev/null +++ b/Source/Plugins/BuilderModes/VisualModes/VisualCeiling.cs @@ -0,0 +1,205 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using System.Drawing; +using System.ComponentModel; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Geometry; +using System.Drawing.Imaging; +using CodeImp.DoomBuilder.Data; +using CodeImp.DoomBuilder.Editing; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.VisualModes; +using CodeImp.DoomBuilder.Windows; + +#endregion + +namespace CodeImp.DoomBuilder.BuilderModes +{ + internal sealed class VisualCeiling : BaseVisualGeometrySector + { + #region ================== Constants + + #endregion + + #region ================== Variables + + #endregion + + #region ================== Properties + + #endregion + + #region ================== Constructor / Setup + + // Constructor + public VisualCeiling(BaseVisualMode mode, VisualSector vs) : base(mode, vs) + { + // We have no destructor + GC.SuppressFinalize(this); + } + + // This builds the geometry. Returns false when no geometry created. + public override bool Setup() + { + WorldVertex[] verts; + WorldVertex v; + Sector s = base.Sector.Sector; + //int brightness = mode.CalculateBrightness(s.Brightness); + int brightness = s.CeilColor.GetColor(); + + // Load floor texture + base.Texture = General.Map.Data.GetFlatImage(s.LongCeilTexture); + if (base.Texture == null) + { + base.Texture = General.Map.Data.MissingTexture3D; + setuponloadedtexture = s.LongCeilTexture; + } + else + { + if (!base.Texture.IsImageLoaded) + setuponloadedtexture = s.LongCeilTexture; + } + + // Make vertices + verts = new WorldVertex[s.Triangles.Vertices.Count]; + for (int i = 0; i < s.Triangles.Vertices.Count; i++) + { + // Use sector brightness for color shading + verts[i].c = brightness; + + // Grid aligned texture coordinates + if (base.Texture.IsImageLoaded) + { + verts[i].u = s.Triangles.Vertices[i].x / base.Texture.ScaledWidth; + verts[i].v = -s.Triangles.Vertices[i].y / base.Texture.ScaledHeight; + } + else + { + verts[i].u = s.Triangles.Vertices[i].x / 64; + verts[i].v = -s.Triangles.Vertices[i].y / 64; + } + + // Vertex coordinates + verts[i].x = s.Triangles.Vertices[i].x; + verts[i].y = s.Triangles.Vertices[i].y; + verts[i].z = (float)s.CeilHeight; + } + + // The sector triangulation created clockwise triangles that + // are right up for the floor. For the ceiling we must flip + // the triangles upside down. + // Swap some vertices to flip all triangles + for (int i = 0; i < verts.Length; i += 3) + { + // Swap + v = verts[i]; + verts[i] = verts[i + 1]; + verts[i + 1] = v; + } + + // Apply vertices + base.SetVertices(verts); + return (verts.Length > 0); + } + + #endregion + + #region ================== Methods + + // Paste texture + public override void OnPasteTexture() + { + if (BuilderPlug.Me.CopiedFlat != null) + { + mode.CreateUndo("Paste ceiling " + BuilderPlug.Me.CopiedFlat); + mode.SetActionResult("Pasted flat " + BuilderPlug.Me.CopiedFlat + " on ceiling."); + SetTexture(BuilderPlug.Me.CopiedFlat); + this.Setup(); + } + } + + // This changes the height + protected override void ChangeHeight(int amount) + { + mode.CreateUndo("Change ceiling height", UndoGroup.CeilingHeightChange, this.Sector.Sector.FixedIndex); + this.Sector.Sector.CeilHeight += amount; + mode.SetActionResult("Changed ceiling height to " + Sector.Sector.CeilHeight + "."); + } + + // This performs a fast test in object picking + public override bool PickFastReject(Vector3D from, Vector3D to, Vector3D dir) + { + float planez = (float)Sector.Sector.CeilHeight; + + // Check if line crosses the z height + if ((from.z < planez) && (to.z > planez)) + { + // Calculate intersection point using the z height + pickrayu = (planez - from.z) / (to.z - from.z); + pickintersect = from + (to - from) * pickrayu; + + // Intersection point within bbox? + RectangleF bbox = Sector.Sector.BBox; + return ((pickintersect.x >= bbox.Left) && (pickintersect.x <= bbox.Right) && + (pickintersect.y >= bbox.Top) && (pickintersect.y <= bbox.Bottom)); + } + else + { + // Not even crossing the z height (or not in the right direction) + return false; + } + } + + // This performs an accurate test for object picking + public override bool PickAccurate(Vector3D from, Vector3D to, Vector3D dir, ref float u_ray) + { + u_ray = pickrayu; + + // Check on which side of the nearest sidedef we are + Sidedef sd = MapSet.NearestSidedef(Sector.Sector.Sidedefs, pickintersect); + float side = sd.Line.SideOfLine(pickintersect); + return (((side <= 0.0f) && sd.IsFront) || ((side > 0.0f) && !sd.IsFront)); + } + + // Return texture name + public override string GetTextureName() + { + return this.Sector.Sector.CeilTexture; + } + + // This changes the texture + protected override void SetTexture(string texturename) + { + this.Sector.Sector.SetCeilTexture(texturename); + General.Map.Data.UpdateUsedTextures(); + this.Setup(); + } + + #endregion + } +} diff --git a/Source/Plugins/BuilderModes/VisualModes/VisualFloor.cs b/Source/Plugins/BuilderModes/VisualModes/VisualFloor.cs new file mode 100644 index 0000000..81cf39e --- /dev/null +++ b/Source/Plugins/BuilderModes/VisualModes/VisualFloor.cs @@ -0,0 +1,192 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using System.Drawing; +using System.ComponentModel; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Geometry; +using System.Drawing.Imaging; +using CodeImp.DoomBuilder.Data; +using CodeImp.DoomBuilder.Editing; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.VisualModes; +using CodeImp.DoomBuilder.Windows; + +#endregion + +namespace CodeImp.DoomBuilder.BuilderModes +{ + internal sealed class VisualFloor : BaseVisualGeometrySector + { + #region ================== Constants + + #endregion + + #region ================== Variables + + #endregion + + #region ================== Properties + + #endregion + + #region ================== Constructor / Setup + + // Constructor + public VisualFloor(BaseVisualMode mode, VisualSector vs) : base(mode, vs) + { + // We have no destructor + GC.SuppressFinalize(this); + } + + // This builds the geometry. Returns false when no geometry created. + public override bool Setup() + { + WorldVertex[] verts; + Sector s = base.Sector.Sector; + //int brightness = mode.CalculateBrightness(s.Brightness); + int brightness = s.FloorColor.GetColor(); + + // Load floor texture + base.Texture = General.Map.Data.GetFlatImage(s.LongFloorTexture); + if (base.Texture == null) + { + base.Texture = General.Map.Data.MissingTexture3D; + setuponloadedtexture = s.LongFloorTexture; + } + else + { + if (!base.Texture.IsImageLoaded) + setuponloadedtexture = s.LongFloorTexture; + } + + // Make vertices + verts = new WorldVertex[s.Triangles.Vertices.Count]; + for (int i = 0; i < s.Triangles.Vertices.Count; i++) + { + // Use sector brightness for color shading + verts[i].c = brightness; + + // Grid aligned texture coordinates + if (base.Texture.IsImageLoaded) + { + verts[i].u = s.Triangles.Vertices[i].x / base.Texture.ScaledWidth; + verts[i].v = -s.Triangles.Vertices[i].y / base.Texture.ScaledHeight; + } + else + { + verts[i].u = s.Triangles.Vertices[i].x / 64; + verts[i].v = -s.Triangles.Vertices[i].y / 64; + } + + // Vertex coordinates + verts[i].x = s.Triangles.Vertices[i].x; + verts[i].y = s.Triangles.Vertices[i].y; + verts[i].z = (float)s.FloorHeight; + } + + // Apply vertices + base.SetVertices(verts); + return (verts.Length > 0); + } + + #endregion + + #region ================== Methods + + // Paste texture + public override void OnPasteTexture() + { + if (BuilderPlug.Me.CopiedFlat != null) + { + mode.CreateUndo("Paste floor " + BuilderPlug.Me.CopiedFlat); + mode.SetActionResult("Pasted flat " + BuilderPlug.Me.CopiedFlat + " on floor."); + SetTexture(BuilderPlug.Me.CopiedFlat); + this.Setup(); + } + } + + // This changes the height + protected override void ChangeHeight(int amount) + { + mode.CreateUndo("Change floor height", UndoGroup.FloorHeightChange, this.Sector.Sector.FixedIndex); + this.Sector.Sector.FloorHeight += amount; + mode.SetActionResult("Changed floor height to " + Sector.Sector.FloorHeight + "."); + } + + // This performs a fast test in object picking + public override bool PickFastReject(Vector3D from, Vector3D to, Vector3D dir) + { + float planez = (float)Sector.Sector.FloorHeight; + + // Check if line crosses the z height + if ((from.z > planez) && (to.z < planez)) + { + // Calculate intersection point using the z height + pickrayu = (planez - from.z) / (to.z - from.z); + pickintersect = from + (to - from) * pickrayu; + + // Intersection point within bbox? + RectangleF bbox = Sector.Sector.BBox; + return ((pickintersect.x >= bbox.Left) && (pickintersect.x <= bbox.Right) && + (pickintersect.y >= bbox.Top) && (pickintersect.y <= bbox.Bottom)); + } + else + { + // Not even crossing the z height (or not in the right direction) + return false; + } + } + + // This performs an accurate test for object picking + public override bool PickAccurate(Vector3D from, Vector3D to, Vector3D dir, ref float u_ray) + { + u_ray = pickrayu; + + // Check on which side of the nearest sidedef we are + Sidedef sd = MapSet.NearestSidedef(Sector.Sector.Sidedefs, pickintersect); + float side = sd.Line.SideOfLine(pickintersect); + return (((side <= 0.0f) && sd.IsFront) || ((side > 0.0f) && !sd.IsFront)); + } + + // Return texture name + public override string GetTextureName() + { + return this.Sector.Sector.FloorTexture; + } + + // This changes the texture + protected override void SetTexture(string texturename) + { + this.Sector.Sector.SetFloorTexture(texturename); + General.Map.Data.UpdateUsedTextures(); + this.Setup(); + } + + #endregion + } +} diff --git a/Source/Plugins/BuilderModes/VisualModes/VisualLower.cs b/Source/Plugins/BuilderModes/VisualModes/VisualLower.cs new file mode 100644 index 0000000..f91c7c9 --- /dev/null +++ b/Source/Plugins/BuilderModes/VisualModes/VisualLower.cs @@ -0,0 +1,207 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using System.Drawing; +using System.ComponentModel; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Geometry; +using System.Drawing.Imaging; +using CodeImp.DoomBuilder.Data; +using CodeImp.DoomBuilder.Editing; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.VisualModes; + +#endregion + +namespace CodeImp.DoomBuilder.BuilderModes +{ + internal sealed class VisualLower : BaseVisualGeometrySidedef + { + #region ================== Constants + + #endregion + + #region ================== Variables + + #endregion + + #region ================== Properties + + #endregion + + #region ================== Constructor / Setup + + // Constructor + public VisualLower(BaseVisualMode mode, VisualSector vs, Sidedef s) : base(mode, vs, s) + { + // We have no destructor + GC.SuppressFinalize(this); + } + + // This builds the geometry. Returns false when no geometry created. + public override bool Setup() + { + //int brightness = mode.CalculateBrightness(Sidedef.Sector.Brightness); + int c1; + int c2; + + if (!Sidedef.Line.IsFlagSet("8388608")) + { + c1 = Sidedef.Sector.ThingColor.GetColor(); + c2 = Sidedef.Sector.ThingColor.GetColor(); + } + else + { + c1 = Sidedef.Sector.TopColor.UnpegLowerLight(Sidedef); + c2 = Sidedef.Sector.LowerColor.GetLowerColor(Sidedef); + } + + // Calculate size of this wall part + float geotop = (float)Sidedef.Other.Sector.FloorHeight; + float geobottom = (float)Sidedef.Sector.FloorHeight; + float geoheight = geotop - geobottom; + if (geoheight > 0.001f) + { + Vector2D t1 = new Vector2D(); + Vector2D t2 = new Vector2D(); + + // Texture given? + if ((Sidedef.LowTexture.Length > 0) && (Sidedef.LowTexture[0] != '-')) + { + // Load texture + base.Texture = General.Map.Data.GetTextureImage(Sidedef.LongLowTexture); + if (base.Texture == null) + { + base.Texture = General.Map.Data.MissingTexture3D; + setuponloadedtexture = Sidedef.LongLowTexture; + } + else + { + if (!base.Texture.IsImageLoaded) + setuponloadedtexture = Sidedef.LongLowTexture; + } + } + else + { + // Use missing texture + base.Texture = General.Map.Data.MissingTexture3D; + setuponloadedtexture = 0; + } + + // Get texture scaled size + Vector2D tsz = new Vector2D(base.Texture.ScaledWidth, base.Texture.ScaledHeight); + + // Determine texture coordinates + // See http://doom.wikia.com/wiki/Texture_alignment + // We just use pixels for coordinates for now + if (Sidedef.Line.IsFlagSet(General.Map.Config.LowerUnpeggedFlag)) + { + // When lower unpegged is set, the lower texture is bound to the bottom + t1.y = (float)Sidedef.Sector.CeilHeight - geotop; + } + + t2.x = t1.x + Sidedef.Line.Length; + t2.y = t1.y + geoheight; + + // Apply texture offset + if (General.Map.Config.ScaledTextureOffsets && !base.Texture.WorldPanning) + { + t1 += new Vector2D(Sidedef.OffsetX * base.Texture.Scale.x, Sidedef.OffsetY * base.Texture.Scale.y); + t2 += new Vector2D(Sidedef.OffsetX * base.Texture.Scale.x, Sidedef.OffsetY * base.Texture.Scale.y); + } + else + { + t1 += new Vector2D(Sidedef.OffsetX, Sidedef.OffsetY); + t2 += new Vector2D(Sidedef.OffsetX, Sidedef.OffsetY); + } + + // Transform pixel coordinates to texture coordinates + t1 /= tsz; + t2 /= tsz; + + // Get world coordinates for geometry + Vector2D v1, v2; + if (Sidedef.IsFront) + { + v1 = Sidedef.Line.Start.Position; + v2 = Sidedef.Line.End.Position; + } + else + { + v1 = Sidedef.Line.End.Position; + v2 = Sidedef.Line.Start.Position; + } + + // Make vertices + WorldVertex[] verts = new WorldVertex[6]; + verts[0] = new WorldVertex(v1.x, v1.y, geobottom, c2, t1.x, t2.y); + verts[1] = new WorldVertex(v1.x, v1.y, geotop, c1, t1.x, t1.y); + verts[2] = new WorldVertex(v2.x, v2.y, geotop, c1, t2.x, t1.y); + verts[3] = verts[0]; + verts[4] = verts[2]; + verts[5] = new WorldVertex(v2.x, v2.y, geobottom, c2, t2.x, t2.y); + + // Keep properties + base.top = geotop; + base.bottom = geobottom; + + // Apply vertices + base.SetVertices(verts); + return true; + } + else + { + // No geometry for invisible wall + base.top = geotop; + base.bottom = geobottom; + WorldVertex[] verts = new WorldVertex[0]; + base.SetVertices(verts); + return false; + } + } + + #endregion + + #region ================== Methods + + // Return texture name + public override string GetTextureName() + { + return this.Sidedef.LowTexture; + } + + // This changes the texture + protected override void SetTexture(string texturename) + { + this.Sidedef.SetTextureLow(texturename); + General.Map.Data.UpdateUsedTextures(); + this.Setup(); + } + + #endregion + } +} diff --git a/Source/Plugins/BuilderModes/VisualModes/VisualMiddleDouble.cs b/Source/Plugins/BuilderModes/VisualModes/VisualMiddleDouble.cs new file mode 100644 index 0000000..14acfd1 --- /dev/null +++ b/Source/Plugins/BuilderModes/VisualModes/VisualMiddleDouble.cs @@ -0,0 +1,236 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using System.Drawing; +using System.ComponentModel; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Geometry; +using System.Drawing.Imaging; +using CodeImp.DoomBuilder.Data; +using CodeImp.DoomBuilder.Editing; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.VisualModes; + +#endregion + +namespace CodeImp.DoomBuilder.BuilderModes +{ + internal sealed class VisualMiddleDouble : BaseVisualGeometrySidedef + { + #region ================== Constants + + #endregion + + #region ================== Variables + + #endregion + + #region ================== Properties + + #endregion + + #region ================== Constructor / Setup + + // Constructor + public VisualMiddleDouble(BaseVisualMode mode, VisualSector vs, Sidedef s) : base(mode, vs, s) + { + // Set render pass + this.RenderPass = RenderPass.Mask; + + // We have no destructor + GC.SuppressFinalize(this); + } + + // This builds the geometry. Returns false when no geometry created. + public override bool Setup() + { + WorldVertex[] verts; + + //int brightness = mode.CalculateBrightness(Sidedef.Sector.Brightness); + int c1; + int c2; + + if (!Sidedef.Line.IsFlagSet("8388608")) + { + c1 = Sidedef.Sector.ThingColor.GetColor(); + c2 = Sidedef.Sector.ThingColor.GetColor(); + } + else + { + c1 = Sidedef.Sector.TopColor.GetColor(); + c2 = Sidedef.Sector.LowerColor.GetColor(); + } + + // Calculate size of this wall part + float geotop = (float)Math.Min(Sidedef.Sector.CeilHeight, Sidedef.Other.Sector.CeilHeight); + float geobottom = (float)Math.Max(Sidedef.Sector.FloorHeight, Sidedef.Other.Sector.FloorHeight); + float geoheight = geotop - geobottom; + if (geoheight > 0.001f) + { + // Texture given? + if ((Sidedef.MiddleTexture.Length > 0) && (Sidedef.MiddleTexture[0] != '-')) + { + Vector2D t1 = new Vector2D(); + Vector2D t2 = new Vector2D(); + float textop, texbottom; + float cliptop = 0.0f; + float clipbottom = 0.0f; + + // Load texture + base.Texture = General.Map.Data.GetTextureImage(Sidedef.LongMiddleTexture); + if (base.Texture == null) + { + base.Texture = General.Map.Data.MissingTexture3D; + setuponloadedtexture = Sidedef.LongMiddleTexture; + } + else + { + if (!base.Texture.IsImageLoaded) + setuponloadedtexture = Sidedef.LongMiddleTexture; + } + + // Get texture scaled size + Vector2D tsz = new Vector2D(base.Texture.ScaledWidth, base.Texture.ScaledHeight); + + // villsa + if (General.Map.FormatInterface.InDoom64Mode) + { + textop = geotop; + } + else + { + // Because the middle texture on a double sided line does not repeat vertically, + // we first determine the visible portion of the texture + if (Sidedef.Line.IsFlagSet(General.Map.Config.LowerUnpeggedFlag)) + textop = geobottom + tsz.y; + else + textop = geotop; + + // Apply texture offset + textop += Sidedef.OffsetY; + } + + // villsa + if (General.Map.FormatInterface.InDoom64Mode) + { + texbottom = geobottom; + } + else + { + // Calculate texture portion bottom + texbottom = textop - tsz.y; + } + + // Clip texture portion by geometry + if (geotop < textop) { cliptop = textop - geotop; textop = geotop; } + if (geobottom > texbottom) { clipbottom = geobottom - texbottom; texbottom = geobottom; } + + // Check if anything is still visible + if ((textop - texbottom) > 0.001f) + { + // Determine texture coordinatess + + // villsa + if (General.Map.FormatInterface.InDoom64Mode) + { + t1.y = geobottom + Sidedef.OffsetY - geotop; + t2.y = geotop + Sidedef.OffsetY - geotop; + } + else + { + t1.y = cliptop; + t2.y = tsz.y - clipbottom; + } + t1.x = Sidedef.OffsetX; + t2.x = t1.x + Sidedef.Line.Length; + + // Transform pixel coordinates to texture coordinates + t1 /= tsz; + t2 /= tsz; + + // Get world coordinates for geometry + Vector2D v1, v2; + if (Sidedef.IsFront) + { + v1 = Sidedef.Line.Start.Position; + v2 = Sidedef.Line.End.Position; + } + else + { + v1 = Sidedef.Line.End.Position; + v2 = Sidedef.Line.Start.Position; + } + + // Make vertices + verts = new WorldVertex[6]; + verts[0] = new WorldVertex(v1.x, v1.y, texbottom, c2, t1.x, t2.y); + verts[1] = new WorldVertex(v1.x, v1.y, textop, c1, t1.x, t1.y); + verts[2] = new WorldVertex(v2.x, v2.y, textop, c1, t2.x, t1.y); + verts[3] = verts[0]; + verts[4] = verts[2]; + verts[5] = new WorldVertex(v2.x, v2.y, texbottom, c2, t2.x, t2.y); + + // Keep properties + base.top = textop; + base.bottom = texbottom; + + // Apply vertices + base.SetVertices(verts); + return true; + } + } + } + + // No geometry for invisible wall + base.top = geotop; + base.bottom = geotop; // bottom same as top so that it has a height of 0 (otherwise it will still be picked up by object picking) + verts = new WorldVertex[0]; + base.SetVertices(verts); + return false; + } + + #endregion + + #region ================== Methods + + // Return texture name + public override string GetTextureName() + { + return this.Sidedef.MiddleTexture; + } + + // This changes the texture + protected override void SetTexture(string texturename) + { + this.Sidedef.SetTextureMid(texturename); + General.Map.Data.UpdateUsedTextures(); + this.Setup(); + } + + #endregion + } +} diff --git a/Source/Plugins/BuilderModes/VisualModes/VisualMiddleSingle.cs b/Source/Plugins/BuilderModes/VisualModes/VisualMiddleSingle.cs new file mode 100644 index 0000000..288fa02 --- /dev/null +++ b/Source/Plugins/BuilderModes/VisualModes/VisualMiddleSingle.cs @@ -0,0 +1,219 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using System.Drawing; +using System.ComponentModel; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Geometry; +using System.Drawing.Imaging; +using CodeImp.DoomBuilder.Data; +using CodeImp.DoomBuilder.Editing; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.VisualModes; + +#endregion + +namespace CodeImp.DoomBuilder.BuilderModes +{ + internal sealed class VisualMiddleSingle : BaseVisualGeometrySidedef + { + #region ================== Constants + + #endregion + + #region ================== Variables + + #endregion + + #region ================== Properties + + #endregion + + #region ================== Constructor / Setup + + // Constructor + public VisualMiddleSingle(BaseVisualMode mode, VisualSector vs, Sidedef s) : base(mode, vs, s) + { + // We have no destructor + GC.SuppressFinalize(this); + } + + // This builds the geometry. Returns false when no geometry created. + public override bool Setup() + { + //int brightness = mode.CalculateBrightness(Sidedef.Sector.Brightness); + int c1; + int c2; + + if (!Sidedef.Line.IsFlagSet("8388608")) + { + c1 = Sidedef.Sector.ThingColor.GetColor(); + c2 = Sidedef.Sector.ThingColor.GetColor(); + } + else + { + c1 = Sidedef.Sector.TopColor.GetColor(); + c2 = Sidedef.Sector.LowerColor.GetColor(); + } + + // Calculate size of this wall part + float geotop = (float)Sidedef.Sector.CeilHeight; + float geobottom = (float)Sidedef.Sector.FloorHeight; + float geoheight = geotop - geobottom; + if (geoheight > 0.001f) + { + Vector2D t1 = new Vector2D(); + Vector2D t2 = new Vector2D(); + + // Texture given? + if ((Sidedef.MiddleTexture.Length > 0) && (Sidedef.MiddleTexture[0] != '-')) + { + // Load texture + base.Texture = General.Map.Data.GetTextureImage(Sidedef.LongMiddleTexture); + if (base.Texture == null) + { + base.Texture = General.Map.Data.MissingTexture3D; + setuponloadedtexture = Sidedef.LongMiddleTexture; + } + else + { + if (!base.Texture.IsImageLoaded) + setuponloadedtexture = Sidedef.LongMiddleTexture; + } + } + else + { + // Use missing texture + base.Texture = General.Map.Data.MissingTexture3D; + setuponloadedtexture = 0; + } + + // Get texture scaled size + Vector2D tsz = new Vector2D(base.Texture.ScaledWidth, base.Texture.ScaledHeight); + + // Determine texture coordinates + // See http://doom.wikia.com/wiki/Texture_alignment + // We just use pixels for coordinates for now + if (Sidedef.Line.IsFlagSet(General.Map.Config.LowerUnpeggedFlag)) + { + // When lower unpegged is set, the middle texture is bound to the bottom + t1.y = tsz.y - geoheight; + + // villsa - if V mirrored, offset Y offset by texture height + if (Sidedef.Line.IsFlagSet("2147483648")) + { + t1.y += tsz.y; + } + } + // villsa + else if (General.Map.FormatInterface.InDoom64Mode && Sidedef.Line.IsFlagSet(General.Map.Config.UpperUnpeggedFlag)) + { + // villsa - when upper unpegged is set, snap the offset by the texture height + t1.y = -geobottom - geoheight; + } + + t2.x = t1.x + Sidedef.Line.Length; + t2.y = t1.y + geoheight; + + // Apply texture offset + if (General.Map.Config.ScaledTextureOffsets && !base.Texture.WorldPanning) + { + t1 += new Vector2D(Sidedef.OffsetX * base.Texture.Scale.x, Sidedef.OffsetY * base.Texture.Scale.y); + t2 += new Vector2D(Sidedef.OffsetX * base.Texture.Scale.x, Sidedef.OffsetY * base.Texture.Scale.y); + } + else + { + t1 += new Vector2D(Sidedef.OffsetX, Sidedef.OffsetY); + t2 += new Vector2D(Sidedef.OffsetX, Sidedef.OffsetY); + } + + // Transform pixel coordinates to texture coordinates + t1 /= tsz; + t2 /= tsz; + + // Get world coordinates for geometry + Vector2D v1, v2; + if (Sidedef.IsFront) + { + v1 = Sidedef.Line.Start.Position; + v2 = Sidedef.Line.End.Position; + } + else + { + v1 = Sidedef.Line.End.Position; + v2 = Sidedef.Line.Start.Position; + } + + // Make vertices + WorldVertex[] verts = new WorldVertex[6]; + verts[0] = new WorldVertex(v1.x, v1.y, geobottom, c2, t1.x, t2.y); + verts[1] = new WorldVertex(v1.x, v1.y, geotop, c1, t1.x, t1.y); + verts[2] = new WorldVertex(v2.x, v2.y, geotop, c1, t2.x, t1.y); + verts[3] = verts[0]; + verts[4] = verts[2]; + verts[5] = new WorldVertex(v2.x, v2.y, geobottom, c2, t2.x, t2.y); + + // Keep properties + base.top = geotop; + base.bottom = geobottom; + + // Apply vertices + base.SetVertices(verts); + return true; + } + else + { + // No geometry for invisible wall + base.top = geotop; + base.bottom = geobottom; + WorldVertex[] verts = new WorldVertex[0]; + base.SetVertices(verts); + return false; + } + } + + #endregion + + #region ================== Methods + + // Return texture name + public override string GetTextureName() + { + return this.Sidedef.MiddleTexture; + } + + // This changes the texture + protected override void SetTexture(string texturename) + { + this.Sidedef.SetTextureMid(texturename); + General.Map.Data.UpdateUsedTextures(); + this.Setup(); + } + + #endregion + } +} diff --git a/Source/Plugins/BuilderModes/VisualModes/VisualSidedefParts.cs b/Source/Plugins/BuilderModes/VisualModes/VisualSidedefParts.cs new file mode 100644 index 0000000..ae74980 --- /dev/null +++ b/Source/Plugins/BuilderModes/VisualModes/VisualSidedefParts.cs @@ -0,0 +1,74 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using CodeImp.DoomBuilder.Windows; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Geometry; +using CodeImp.DoomBuilder.Editing; +using CodeImp.DoomBuilder.VisualModes; + +#endregion + +namespace CodeImp.DoomBuilder.BuilderModes +{ + internal struct VisualSidedefParts + { + // Members + public VisualUpper upper; + public VisualLower lower; + public VisualMiddleDouble middledouble; + public VisualMiddleSingle middlesingle; + + // Constructor + public VisualSidedefParts(VisualUpper u, VisualLower l, VisualMiddleDouble m) + { + this.upper = u; + this.lower = l; + this.middledouble = m; + this.middlesingle = null; + } + + // Constructor + public VisualSidedefParts(VisualMiddleSingle m) + { + this.upper = null; + this.lower = null; + this.middledouble = null; + this.middlesingle = m; + } + + // This calls Setup() on all parts + public void SetupAllParts() + { + if (lower != null) lower.Setup(); + if (middledouble != null) middledouble.Setup(); + if (middlesingle != null) middlesingle.Setup(); + if (upper != null) upper.Setup(); + } + } +} diff --git a/Source/Plugins/BuilderModes/VisualModes/VisualUpper.cs b/Source/Plugins/BuilderModes/VisualModes/VisualUpper.cs new file mode 100644 index 0000000..0467acb --- /dev/null +++ b/Source/Plugins/BuilderModes/VisualModes/VisualUpper.cs @@ -0,0 +1,219 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using System.Drawing; +using System.ComponentModel; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Geometry; +using System.Drawing.Imaging; +using CodeImp.DoomBuilder.Data; +using CodeImp.DoomBuilder.Editing; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.VisualModes; + +#endregion + +namespace CodeImp.DoomBuilder.BuilderModes +{ + internal sealed class VisualUpper : BaseVisualGeometrySidedef + { + #region ================== Constants + + #endregion + + #region ================== Variables + + #endregion + + #region ================== Properties + + #endregion + + #region ================== Constructor / Setup + + // Constructor + public VisualUpper(BaseVisualMode mode, VisualSector vs, Sidedef s) : base(mode, vs, s) + { + // We have no destructor + GC.SuppressFinalize(this); + } + + // This builds the geometry. Returns false when no geometry created. + public override bool Setup() + { + //int brightness = mode.CalculateBrightness(Sidedef.Sector.Brightness); + int c1; + int c2; + + if (!Sidedef.Line.IsFlagSet("8388608")) + { + c1 = Sidedef.Sector.ThingColor.GetColor(); + c2 = Sidedef.Sector.ThingColor.GetColor(); + } + else + { + c1 = Sidedef.Sector.TopColor.GetTopColor(Sidedef); + c2 = Sidedef.Sector.LowerColor.UnpegUpperLight(Sidedef); + } + + // Calculate size of this wall part + float geotop = (float)Sidedef.Sector.CeilHeight; + float geobottom = (float)Sidedef.Other.Sector.CeilHeight; + float geoheight = geotop - geobottom; + if (geoheight > 0.001f) + { + Vector2D t1 = new Vector2D(); + Vector2D t2 = new Vector2D(); + + // Texture given? + if ((Sidedef.HighTexture.Length > 0) && (Sidedef.HighTexture[0] != '-')) + { + // Load texture + base.Texture = General.Map.Data.GetTextureImage(Sidedef.LongHighTexture); + if (base.Texture == null) + { + base.Texture = General.Map.Data.MissingTexture3D; + setuponloadedtexture = Sidedef.LongHighTexture; + } + else + { + if (!base.Texture.IsImageLoaded) + setuponloadedtexture = Sidedef.LongHighTexture; + } + } + else + { + // Use missing texture + base.Texture = General.Map.Data.MissingTexture3D; + setuponloadedtexture = 0; + } + + // Get texture scaled size + Vector2D tsz = new Vector2D(base.Texture.ScaledWidth, base.Texture.ScaledHeight); + + // Determine texture coordinates + // See http://doom.wikia.com/wiki/Texture_alignment + // We just use pixels for coordinates for now + if (!Sidedef.Line.IsFlagSet(General.Map.Config.UpperUnpeggedFlag)) + { + // When upper unpegged is NOT set, the upper texture is bound to the bottom + t1.y = tsz.y - geoheight; + } + + // villsa + if (General.Map.FormatInterface.InDoom64Mode) + { + // if V mirrored, offset Y offset by texture height + if (Sidedef.Line.IsFlagSet("2147483648") && + !Sidedef.Line.IsFlagSet(General.Map.Config.UpperUnpeggedFlag)) + { + t1.y += tsz.y; + } + + } + + t2.x = t1.x + Sidedef.Line.Length; + t2.y = t1.y + geoheight; + + // Apply texture offset + if (General.Map.Config.ScaledTextureOffsets && !base.Texture.WorldPanning) + { + t1 += new Vector2D(Sidedef.OffsetX * base.Texture.Scale.x, Sidedef.OffsetY * base.Texture.Scale.y); + t2 += new Vector2D(Sidedef.OffsetX * base.Texture.Scale.x, Sidedef.OffsetY * base.Texture.Scale.y); + } + else + { + t1 += new Vector2D(Sidedef.OffsetX, Sidedef.OffsetY); + t2 += new Vector2D(Sidedef.OffsetX, Sidedef.OffsetY); + } + + // Transform pixel coordinates to texture coordinates + t1 /= tsz; + t2 /= tsz; + + // Get world coordinates for geometry + Vector2D v1, v2; + if (Sidedef.IsFront) + { + v1 = Sidedef.Line.Start.Position; + v2 = Sidedef.Line.End.Position; + } + else + { + v1 = Sidedef.Line.End.Position; + v2 = Sidedef.Line.Start.Position; + } + + // Make vertices + WorldVertex[] verts = new WorldVertex[6]; + verts[0] = new WorldVertex(v1.x, v1.y, geobottom, c2, t1.x, t2.y); + verts[1] = new WorldVertex(v1.x, v1.y, geotop, c1, t1.x, t1.y); + verts[2] = new WorldVertex(v2.x, v2.y, geotop, c1, t2.x, t1.y); + verts[3] = verts[0]; + verts[4] = verts[2]; + verts[5] = new WorldVertex(v2.x, v2.y, geobottom, c2, t2.x, t2.y); + + // Keep properties + base.top = geotop; + base.bottom = geobottom; + + // Apply vertices + base.SetVertices(verts); + return true; + } + else + { + // No geometry for invisible wall + base.top = geotop; + base.bottom = geobottom; + WorldVertex[] verts = new WorldVertex[0]; + base.SetVertices(verts); + return false; + } + } + + #endregion + + #region ================== Methods + + // Return texture name + public override string GetTextureName() + { + return this.Sidedef.HighTexture; + } + + // This changes the texture + protected override void SetTexture(string texturename) + { + this.Sidedef.SetTextureHigh(texturename); + General.Map.Data.UpdateUsedTextures(); + this.Setup(); + } + + #endregion + } +} diff --git a/Source/Plugins/CopyPasteSectorProps/Actions.cfg b/Source/Plugins/CopyPasteSectorProps/Actions.cfg new file mode 100644 index 0000000..433bba1 --- /dev/null +++ b/Source/Plugins/CopyPasteSectorProps/Actions.cfg @@ -0,0 +1,62 @@ + +// +// This file defines which actions there are, what description they have and +// some behaviour options. The Doom Builder core will bind to these actions +// with delegates (function pointers) where you use the BeginAction and +// EndAction attributes. This file must be named Actions.cfg and must be +// included in the plugin project as "Embedded Resource". +// + +// +// Options: +// +// allowkeys: Allows the user to bind standard keys to this action. +// allowmouse: Allows the user to bind mouse buttons to this action. +// allowscroll: Allows the user to bind the scrollwheel to this action. +// disregardshift: This action will trigger regardless if Shift or Control is used. +// repeat: BeginAction will be called for automatic key repetition. +// default: Default key is only used when the action is loaded for the first +// time and the default key is not used by any other action. +// +// allowkeys and allowmouse are true by default, the others are false by default. +// + +copysectorprops +{ + title = "Copy sector properties"; + category = "sectors"; + description = "Copies sector properties."; + allowkeys = true; + allowmouse = true; + allowscroll = true; +} + +pastesectorprops +{ + title = "Paste sector properties"; + category = "sectors"; + description = "Pastes sector properties."; + allowkeys = true; + allowmouse = true; + allowscroll = true; +} + +copysectorcolors +{ + title = "Copy sector colors"; + category = "sectors"; + description = "Copies sector colors."; + allowkeys = true; + allowmouse = true; + allowscroll = true; +} + +pastesectorcolors +{ + title = "Paste sector colors"; + category = "sectors"; + description = "Pastes sector colors."; + allowkeys = true; + allowmouse = true; + allowscroll = true; +} \ No newline at end of file diff --git a/Source/Plugins/CopyPasteSectorProps/BuilderPlug.cs b/Source/Plugins/CopyPasteSectorProps/BuilderPlug.cs new file mode 100644 index 0000000..9584fc2 --- /dev/null +++ b/Source/Plugins/CopyPasteSectorProps/BuilderPlug.cs @@ -0,0 +1,341 @@ + +#region ================== Copyright (c) 2009 Boris Iwanski + +/* + * Copyright (c) 2009 Boris Iwanski + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using CodeImp.DoomBuilder.Windows; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Geometry; +using System.Drawing; +using CodeImp.DoomBuilder.Editing; +using CodeImp.DoomBuilder.Plugins; +using CodeImp.DoomBuilder.Actions; +using CodeImp.DoomBuilder.Types; +using CodeImp.DoomBuilder.Config; +using CodeImp.DoomBuilder.Data; + +#endregion + +namespace CodeImp.DoomBuilder.CopyPasteSectorProps +{ + // + // MANDATORY: The plug! + // This is an important class to the Doom Builder core. Every plugin must + // have exactly 1 class that inherits from Plug. When the plugin is loaded, + // this class is instantiated and used to receive events from the core. + // Make sure the class is public, because only public classes can be seen + // by the core. + // + + public class BuilderPlug : Plug + { + // Static instance. We can't use a real static class, because BuilderPlug must + // be instantiated by the core, so we keep a static reference. (this technique + // should be familiar to object-oriented programmers) + private static BuilderPlug me; + + // Static property to access the BuilderPlug + public static BuilderPlug Me { get { return me; } } + + // These variables will store the properties we want to copy + private int floorHeight; + private int ceilHeight; + private string floorTexture; + private string ceilTexture; + private int brightness; + private int effect; + private int tag; + private Lights ceilColor; // villsa + private Lights floorColor; // villsa + private Lights thingColor; // villsa + private Lights upperColor; // villsa + private Lights lowerColor; // villsa + private UniFields fields; + + // This is set to true to know that we copied sector properties. + // If this is false, the variables above are uninitialized. + bool didCopyProps = false; + bool didCopyColors = false; + + // This event is called when the plugin is initialized + public override void OnInitialize() + { + base.OnInitialize(); + + // This binds the methods in this class that have the BeginAction + // and EndAction attributes with their actions. Without this, the + // attributes are useless. Note that in classes derived from EditMode + // this is not needed, because they are bound automatically when the + // editing mode is engaged. + General.Actions.BindMethods(this); + + // Keep a static reference + me = this; + } + + // This is called when the plugin is terminated + public override void Dispose() + { + base.Dispose(); + + // This must be called to remove bound methods for actions. + General.Actions.UnbindMethods(this); + } + + #region ================== Actions + + // This is the method that will be called when the "copysectorprops" action is used by the user. + // The actions are defined in the Actions.cfg file (the name must be exactly that) and the + // BeginAction attribute indicates that this method must be called when the key for this + // action is pressed. You can use the EndAction attribute for methods that must be called when + // the key for an action is released. See above for the BindMethods method call which must be + // called in order for these methods to work. + [BeginAction("copysectorprops")] + public void CopySectorProps() + { + // Only make this action possible if a map is actually opened and the we are in sectors mode + if (General.Editing.Mode == null || General.Editing.Mode.Attributes.SwitchAction != "sectorsmode") + { + return; + } + + // Make sure a sector is highlighted + if (!(General.Editing.Mode.HighlightedObject is Sector)) + { + // Show a warning in the status bar + General.Interface.DisplayStatus(StatusType.Warning, "Please highlight a sector to copy the properties from"); + return; + } + + // Copy the properties from the sector + Sector s = (Sector)General.Editing.Mode.HighlightedObject; + floorHeight = s.FloorHeight; + ceilHeight = s.CeilHeight; + floorTexture = s.FloorTexture; + ceilTexture = s.CeilTexture; + brightness = s.Brightness; + effect = s.Effect; + tag = s.Tag; + ceilColor = s.CeilColor; // villsa + floorColor = s.FloorColor; // villsa + thingColor = s.ThingColor; // villsa + upperColor = s.TopColor; // villsa + lowerColor = s.LowerColor; // villsa + + // Remember that we copied the properties + didCopyProps = true; + + // Let the user know that copying worked + General.Interface.DisplayStatus(StatusType.Action, "Copied sector properties."); + } + + [BeginAction("copysectorcolors")] + public void CopySectorColors() + { + // Only make this action possible if a map is actually opened and the we are in sectors mode + if (General.Editing.Mode == null || General.Editing.Mode.Attributes.SwitchAction != "sectorsmode") + { + return; + } + + // Make sure a sector is highlighted + if (!(General.Editing.Mode.HighlightedObject is Sector)) + { + // Show a warning in the status bar + General.Interface.DisplayStatus(StatusType.Warning, "Please highlight a sector to copy the colors from"); + return; + } + + // Copy the properties from the sector + Sector s = (Sector)General.Editing.Mode.HighlightedObject; + ceilColor = s.CeilColor; // villsa + floorColor = s.FloorColor; // villsa + thingColor = s.ThingColor; // villsa + upperColor = s.TopColor; // villsa + lowerColor = s.LowerColor; // villsa + + // Remember that we copied the colors + didCopyColors = true; + + // Let the user know that copying worked + General.Interface.DisplayStatus(StatusType.Action, "Copied sector colors."); + } + + // This is the method that will be called when the "pastesectorprops" action is used by the user. + [BeginAction("pastesectorprops")] + public void PasteSectorProps() + { + // Collection used to store the sectors we want to paste to + List sectors = new List(); + + // Just for fun we want to let the user know to how many sectors he/she/it pasted + int pasteCount = 0; + + // Only make this action possible if a map is actually opened and we are in sectors mode + if (General.Editing.Mode == null || General.Editing.Mode.Attributes.SwitchAction != "sectorsmode") + { + return; + } + + // Make sure there's at least one selected or highlighted sector we can paste the properties to + if (General.Map.Map.GetSelectedSectors(true).Count == 0 && !(General.Editing.Mode.HighlightedObject is Sector)) + { + // Show a warning in the status bar + General.Interface.DisplayStatus(StatusType.Warning, "Please select or highlight at least 1 sector to paste the properties to."); + return; + } + + // Check if there's actually a copied sector to get the properties from + if (didCopyProps == false) + { + // Show a warning in the status bar + General.Interface.DisplayStatus(StatusType.Warning, "Can't paste properties. You need to copy the properties first."); + return; + } + + // If there are selected sectors only paste to them + if (General.Map.Map.GetSelectedSectors(true).Count != 0) + { + ICollection selectedsectors = General.Map.Map.GetSelectedSectors(true); + foreach (Sector s in selectedsectors) + { + sectors.Add(s); + pasteCount++; + } + } + // No selected sectors. paste to the highlighted one + else + { + sectors.Add((Sector)General.Editing.Mode.HighlightedObject); + pasteCount++; + } + + // Make the action undo-able + General.Map.UndoRedo.CreateUndo("Paste sector properties"); + + // Set the properties of all selected sectors + foreach (Sector s in sectors) + { + // Heights + s.FloorHeight = floorHeight; + s.CeilHeight = ceilHeight; + + // Textures + s.SetFloorTexture(floorTexture); + s.SetCeilTexture(ceilTexture); + + // Other stuff + s.Brightness = brightness; + s.Effect = effect; + s.Tag = tag; + s.CeilColor = ceilColor; // villsa + s.FloorColor = floorColor; // villsa + s.ThingColor = thingColor; // villsa + s.TopColor = upperColor; // villsa + s.LowerColor = lowerColor; // villsa + } + + // Redraw to make the changes visible + General.Map.Map.Update(); + General.Interface.RedrawDisplay(); + + // Let the user know to how many sectors the properties were copied + General.Interface.DisplayStatus(StatusType.Action, "Pasted sector properties to " + pasteCount.ToString() + " sector(s)."); + } + + [BeginAction("pastesectorcolors")] + public void PasteSectorColors() + { + // Collection used to store the sectors we want to paste to + List sectors = new List(); + + // Just for fun we want to let the user know to how many sectors he/she/it pasted + int pasteCount = 0; + + // Only make this action possible if a map is actually opened and we are in sectors mode + if (General.Editing.Mode == null || General.Editing.Mode.Attributes.SwitchAction != "sectorsmode") + { + return; + } + + // Make sure there's at least one selected or highlighted sector we can paste the properties to + if (General.Map.Map.GetSelectedSectors(true).Count == 0 && !(General.Editing.Mode.HighlightedObject is Sector)) + { + // Show a warning in the status bar + General.Interface.DisplayStatus(StatusType.Warning, "Please select or highlight at least 1 sector to paste the colors to."); + return; + } + + // Check if there's actually a copied sector to get the properties from + if (didCopyColors == false) + { + // Show a warning in the status bar + General.Interface.DisplayStatus(StatusType.Warning, "Can't paste colors. You need to copy the colors first."); + return; + } + + // If there are selected sectors only paste to them + if (General.Map.Map.GetSelectedSectors(true).Count != 0) + { + ICollection selectedsectors = General.Map.Map.GetSelectedSectors(true); + foreach (Sector s in selectedsectors) + { + sectors.Add(s); + pasteCount++; + } + } + // No selected sectors. paste to the highlighted one + else + { + sectors.Add((Sector)General.Editing.Mode.HighlightedObject); + pasteCount++; + } + + // Make the action undo-able + General.Map.UndoRedo.CreateUndo("Paste sector colors"); + + // Set the properties of all selected sectors + foreach (Sector s in sectors) + { + s.CeilColor = ceilColor; // villsa + s.FloorColor = floorColor; // villsa + s.ThingColor = thingColor; // villsa + s.TopColor = upperColor; // villsa + s.LowerColor = lowerColor; // villsa + } + + // Redraw to make the changes visible + General.Map.Map.Update(); + General.Interface.RedrawDisplay(); + + // Let the user know to how many sectors the properties were copied + General.Interface.DisplayStatus(StatusType.Action, "Pasted sector colors to " + pasteCount.ToString() + " sector(s)."); + } + + #endregion + } +} diff --git a/Source/Plugins/CopyPasteSectorProps/CopyPasteSectorProperties.csproj b/Source/Plugins/CopyPasteSectorProps/CopyPasteSectorProperties.csproj new file mode 100644 index 0000000..ed18ae0 --- /dev/null +++ b/Source/Plugins/CopyPasteSectorProps/CopyPasteSectorProperties.csproj @@ -0,0 +1,74 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {A5F93B70-18D9-4F3C-9B72-BC8B5B13998E} + Library + Properties + CodeImp.DoomBuilder.CopyPasteSectorProps + CopyPasteSectorProps + v3.5 + 512 + + + + + 3.5 + + + true + ..\..\..\Build\Plugins\ + DEBUG;TRACE + true + full + x86 + prompt + + + ..\..\..\Build\Plugins\ + true + true + pdbonly + x86 + prompt + + + + + 3.5 + + + + + + + + + + + {818B3D10-F791-4C3F-9AF5-BB2D0079B63C} + Builder + False + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Source/Plugins/CopyPasteSectorProps/CopyPasteSectorProperties.suo b/Source/Plugins/CopyPasteSectorProps/CopyPasteSectorProperties.suo new file mode 100644 index 0000000000000000000000000000000000000000..ba4cd1c0bbee6300dfc602f34a182a31b2f7d1c5 GIT binary patch literal 2560 zcmeHHyG{a85IqZ`u~HifE#wPKw4^Xmq_BdBl_4=3URlM3u$V~gZx|D`6AEc9jEym| zk;cTvL@PhQ!V1pd*{yFJ=3_JxId^*KHv?_!;h9Zis3H1LRpqY zChscm*I5H}b?>Ub(18^c_>~bt6gqhcl@63Y#e)bqnD_VD z(O7%hfAn1`5_fJ7k~lLKuz_V}&r*tFr_U+9?jWa7cN==W?i$CJMKNr%>O3^&mz6_; zy2_Qoocngi6Z%GeDZOT&@KOF*CyTql{ED0>vsn7z9{K7SqIhPjf_vF3Zz6JlTxPz= z9{v9g6i1H3H@nlZlgoPM^r))y>AysKh#-t4mWbUHv9v|p$Q&^^QB+E4@&s*VerW1FkU6{WN(JrCL;wFVQL!qsSfWTqW9*Sg^br?K*8U)Ure} zaQA%Mlyc-9-!JR%PuzczozvKhH0zWK^VD-z9R{ymyI7qXBNv + + + Debug + AnyCPU + 8.0.50727 + 2.0 + {FBC0A503-9152-4BE2-9B5C-128FFD0B0D3F} + Library + Properties + CodeImp.DoomBuilder.Statistics + Statistics + v3.5 + 512 + + + + + 3.5 + + + true + ..\..\..\Build\Plugins\ + DEBUG;TRACE + true + full + x86 + prompt + false + + + ..\..\..\Build\Plugins\ + true + true + pdbonly + x86 + prompt + false + + + + + 3.5 + + + + + + + + + Form + + + StatisticsForm.cs + + + + + + {818B3D10-F791-4C3F-9AF5-BB2D0079B63C} + Builder + False + + + + + + + + + StatisticsForm.cs + Designer + + + + + + + + + + + \ No newline at end of file diff --git a/Source/Plugins/Statistics/StatisticsForm.Designer.cs b/Source/Plugins/Statistics/StatisticsForm.Designer.cs new file mode 100644 index 0000000..89aa83b --- /dev/null +++ b/Source/Plugins/Statistics/StatisticsForm.Designer.cs @@ -0,0 +1,196 @@ +namespace CodeImp.DoomBuilder.Statistics +{ + partial class StatisticsForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.label1 = new System.Windows.Forms.Label(); + this.label2 = new System.Windows.Forms.Label(); + this.label3 = new System.Windows.Forms.Label(); + this.label4 = new System.Windows.Forms.Label(); + this.label5 = new System.Windows.Forms.Label(); + this.verticescount = new System.Windows.Forms.Label(); + this.linedefscount = new System.Windows.Forms.Label(); + this.sidedefscount = new System.Windows.Forms.Label(); + this.sectorscount = new System.Windows.Forms.Label(); + this.thingscount = new System.Windows.Forms.Label(); + this.closebutton = new System.Windows.Forms.Button(); + this.SuspendLayout(); + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(33, 25); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(51, 14); + this.label1.TabIndex = 0; + this.label1.Text = "Vertices:"; + this.label1.TextAlign = System.Drawing.ContentAlignment.TopRight; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(32, 47); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(52, 14); + this.label2.TabIndex = 1; + this.label2.Text = "Linedefs:"; + this.label2.TextAlign = System.Drawing.ContentAlignment.TopRight; + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Location = new System.Drawing.Point(31, 69); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(53, 14); + this.label3.TabIndex = 2; + this.label3.Text = "Sidedefs:"; + this.label3.TextAlign = System.Drawing.ContentAlignment.TopRight; + // + // label4 + // + this.label4.AutoSize = true; + this.label4.Location = new System.Drawing.Point(36, 91); + this.label4.Name = "label4"; + this.label4.Size = new System.Drawing.Size(48, 14); + this.label4.TabIndex = 3; + this.label4.Text = "Sectors:"; + this.label4.TextAlign = System.Drawing.ContentAlignment.TopRight; + // + // label5 + // + this.label5.AutoSize = true; + this.label5.Location = new System.Drawing.Point(42, 113); + this.label5.Name = "label5"; + this.label5.Size = new System.Drawing.Size(42, 14); + this.label5.TabIndex = 4; + this.label5.Text = "Things:"; + this.label5.TextAlign = System.Drawing.ContentAlignment.TopRight; + // + // verticescount + // + this.verticescount.AutoSize = true; + this.verticescount.Location = new System.Drawing.Point(101, 25); + this.verticescount.Name = "verticescount"; + this.verticescount.Size = new System.Drawing.Size(49, 14); + this.verticescount.TabIndex = 5; + this.verticescount.Text = "0000000"; + // + // linedefscount + // + this.linedefscount.AutoSize = true; + this.linedefscount.Location = new System.Drawing.Point(101, 47); + this.linedefscount.Name = "linedefscount"; + this.linedefscount.Size = new System.Drawing.Size(49, 14); + this.linedefscount.TabIndex = 6; + this.linedefscount.Text = "0000000"; + // + // sidedefscount + // + this.sidedefscount.AutoSize = true; + this.sidedefscount.Location = new System.Drawing.Point(101, 69); + this.sidedefscount.Name = "sidedefscount"; + this.sidedefscount.Size = new System.Drawing.Size(49, 14); + this.sidedefscount.TabIndex = 7; + this.sidedefscount.Text = "0000000"; + // + // sectorscount + // + this.sectorscount.AutoSize = true; + this.sectorscount.Location = new System.Drawing.Point(101, 91); + this.sectorscount.Name = "sectorscount"; + this.sectorscount.Size = new System.Drawing.Size(49, 14); + this.sectorscount.TabIndex = 8; + this.sectorscount.Text = "0000000"; + // + // thingscount + // + this.thingscount.AutoSize = true; + this.thingscount.Location = new System.Drawing.Point(101, 113); + this.thingscount.Name = "thingscount"; + this.thingscount.Size = new System.Drawing.Size(49, 14); + this.thingscount.TabIndex = 9; + this.thingscount.Text = "0000000"; + // + // closebutton + // + this.closebutton.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.closebutton.Location = new System.Drawing.Point(34, 166); + this.closebutton.Name = "closebutton"; + this.closebutton.Size = new System.Drawing.Size(116, 26); + this.closebutton.TabIndex = 10; + this.closebutton.Text = "Close"; + this.closebutton.UseVisualStyleBackColor = true; + this.closebutton.Click += new System.EventHandler(this.closebutton_Click); + // + // StatisticsForm + // + this.AcceptButton = this.closebutton; + this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; + this.CancelButton = this.closebutton; + this.ClientSize = new System.Drawing.Size(184, 209); + this.Controls.Add(this.closebutton); + this.Controls.Add(this.thingscount); + this.Controls.Add(this.sectorscount); + this.Controls.Add(this.sidedefscount); + this.Controls.Add(this.linedefscount); + this.Controls.Add(this.verticescount); + this.Controls.Add(this.label5); + this.Controls.Add(this.label4); + this.Controls.Add(this.label3); + this.Controls.Add(this.label2); + this.Controls.Add(this.label1); + this.DoubleBuffered = true; + this.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedToolWindow; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "StatisticsForm"; + this.StartPosition = System.Windows.Forms.FormStartPosition.Manual; + this.Text = "Statistics"; + this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.StatisticsForm_FormClosing); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Label label1; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.Label label3; + private System.Windows.Forms.Label label4; + private System.Windows.Forms.Label label5; + private System.Windows.Forms.Label verticescount; + private System.Windows.Forms.Label linedefscount; + private System.Windows.Forms.Label sidedefscount; + private System.Windows.Forms.Label sectorscount; + private System.Windows.Forms.Label thingscount; + private System.Windows.Forms.Button closebutton; + } +} \ No newline at end of file diff --git a/Source/Plugins/Statistics/StatisticsForm.cs b/Source/Plugins/Statistics/StatisticsForm.cs new file mode 100644 index 0000000..3ab0f32 --- /dev/null +++ b/Source/Plugins/Statistics/StatisticsForm.cs @@ -0,0 +1,88 @@ + +#region ================== Copyright (c) 2009 Pascal vd Heiden + +/* + * Copyright (c) 2009 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Windows.Forms; + +#endregion + +namespace CodeImp.DoomBuilder.Statistics +{ + public partial class StatisticsForm : Form + { + // Constructor + public StatisticsForm() + { + InitializeComponent(); + } + + // We're going to use this to show the form + public void ShowWindow(Form owner) + { + // Position this window in the left-top corner of owner + this.Location = new Point(owner.Location.X + 20, owner.Location.Y + 90); + + // Update statistics + UpdateStats(); + + // Show it + base.Show(owner); + } + + // Form is closing event + private void StatisticsForm_FormClosing(object sender, FormClosingEventArgs e) + { + // When the user is closing the window we want to cancel this, because it + // would also unload (dispose) the form. We only want to hide the window + // so that it can be re-used next time when this editing mode is activated. + if (e.CloseReason == CloseReason.UserClosing) + { + // Just cancel the editing mode. This will automatically call + // OnCancel() which will switch to the previous mode and in turn + // calls OnDisengage() which hides this window. + General.Editing.CancelMode(); + e.Cancel = true; + } + } + + // This function updates all statistics on the form + public void UpdateStats() + { + // You can access the Doom Builder core through the static General class. + // General.Map gives you access the manager of the map that is being edited. + // General.Map.Map gives you access to the low level map structure. + + verticescount.Text = General.Map.Map.Vertices.Count.ToString(); + linedefscount.Text = General.Map.Map.Linedefs.Count.ToString(); + sidedefscount.Text = General.Map.Map.Sidedefs.Count.ToString(); + sectorscount.Text = General.Map.Map.Sectors.Count.ToString(); + thingscount.Text = General.Map.Map.Things.Count.ToString(); + } + + // Close button clicked + private void closebutton_Click(object sender, EventArgs e) + { + General.Editing.CancelMode(); + } + } +} diff --git a/Source/Plugins/Statistics/StatisticsForm.resx b/Source/Plugins/Statistics/StatisticsForm.resx new file mode 100644 index 0000000..034a282 --- /dev/null +++ b/Source/Plugins/Statistics/StatisticsForm.resx @@ -0,0 +1,156 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + \ No newline at end of file diff --git a/Source/Plugins/Statistics/StatisticsIcon.png b/Source/Plugins/Statistics/StatisticsIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..acff16ce155a10e7fa172c53b22b09abee4be07b GIT binary patch literal 280 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!60wlNoGJgf6I14-?iy0WWg+Q3`(%rg0Ai)yX zh!W@g+}zZ>5(b7&tzI^e22U5qkcv6UKh8JE3r%R#&hW|j6aRhxulk?=AN~LNfAhcP ze~ue0eD{Cf|J488dQam{?U-Dn`AxR1$*cd}f8l@6RU9f3XQ=&2*SKN+zy8*L7EYT! zpP%->>z{Dha3!g`{Nbrx$*zN{}q=sxSam~{{MXsOXBPVE2E00`}O=AHn3ip z`RfH&lTFRP-T%w)q;Ed|XV%5~z5k