From 1b415c5ea0ba62197e1f45aee3c050054c66ffda Mon Sep 17 00:00:00 2001 From: pwndp Date: Tue, 22 Jan 2019 03:01:41 +0100 Subject: [PATCH] Initial version --- .gitignore | 341 +++ MonocleEngineDemo/Monocle/Colliders/Circle.cs | 103 + .../Monocle/Colliders/Collide.cs | 440 ++++ .../Monocle/Colliders/Collider.cs | 342 +++ .../Monocle/Colliders/ColliderList.cs | 266 ++ MonocleEngineDemo/Monocle/Colliders/Grid.cs | 489 ++++ MonocleEngineDemo/Monocle/Colliders/Hitbox.cs | 161 ++ .../Monocle/Components/CollidableComponent.cs | 324 +++ .../Monocle/Components/Component.cs | 98 + .../Components/Graphics/GraphicsComponent.cs | 99 + .../Monocle/Components/Graphics/Image.cs | 65 + .../Components/Graphics/ParticleEmitter.cs | 96 + .../Monocle/Components/Graphics/PixelText.cs | 125 + .../Monocle/Components/Graphics/Sprite.cs | 520 ++++ .../Components/Graphics/Spritesheet.cs | 243 ++ .../Components/Graphics/Text/NumberText.cs | 70 + .../Components/Graphics/Text/OutlineText.cs | 38 + .../Monocle/Components/Graphics/Text/Text.cs | 115 + .../Components/Graphics/Text/TimerText.cs | 136 + .../Monocle/Components/Graphics/TileGrid.cs | 196 ++ .../Monocle/Components/Logic/Alarm.cs | 110 + .../Monocle/Components/Logic/Coroutine.cs | 84 + .../Components/Logic/CoroutineHolder.cs | 90 + .../Monocle/Components/Logic/CounterSet.cs | 50 + .../Monocle/Components/Logic/Shaker.cs | 90 + .../Monocle/Components/Logic/ShakerList.cs | 94 + .../Monocle/Components/Logic/SineWave.cs | 107 + .../Monocle/Components/Logic/StateMachine.cs | 183 ++ .../Monocle/Components/Logic/Tween.cs | 213 ++ .../Monocle/Components/Logic/Wiggler.cs | 131 + .../Content/Monocle/MonocleDefault.spritefont | 60 + .../Content/Monocle/MonocleDefault.xnb | Bin 0 -> 21678 bytes MonocleEngineDemo/Monocle/Engine.cs | 411 +++ MonocleEngineDemo/Monocle/Entity.cs | 1170 +++++++++ MonocleEngineDemo/Monocle/Graphics/Atlas.cs | 417 ++++ .../Monocle/Graphics/MTexture.cs | 827 +++++++ .../Monocle/Graphics/SpriteBank.cs | 74 + .../Monocle/Graphics/SpriteData.cs | 163 ++ MonocleEngineDemo/Monocle/Graphics/Tileset.cs | 60 + MonocleEngineDemo/Monocle/Input/MInput.cs | 872 +++++++ .../Monocle/Input/VirtualAxis.cs | 249 ++ .../Monocle/Input/VirtualButton.cs | 902 +++++++ .../Monocle/Input/VirtualInput.cs | 36 + .../Monocle/Input/VirtualIntegerAxis.cs | 51 + .../Monocle/Input/VirtualJoystick.cs | 257 ++ .../InternalUtilities/ComponentList.cs | 252 ++ .../Monocle/InternalUtilities/EntityList.cs | 285 +++ .../Monocle/InternalUtilities/RendererList.cs | 93 + .../Monocle/InternalUtilities/TagLists.cs | 74 + .../Monocle/Monocle.MonoGame.csproj | 132 + MonocleEngineDemo/Monocle/Monocle.csproj | 185 ++ .../Monocle/Particles/Particle.cs | 139 ++ .../Monocle/Particles/ParticleSystem.cs | 134 + .../Monocle/Particles/ParticleType.cs | 159 ++ .../Monocle/Properties/AssemblyInfo.cs | 33 + .../Monocle/Renderers/EverythingRenderer.cs | 43 + .../Monocle/Renderers/Renderer.cs | 12 + .../Monocle/Renderers/SingleTagRenderer.cs | 46 + .../Monocle/Renderers/TagExcludeRenderer.cs | 51 + MonocleEngineDemo/Monocle/Scene.cs | 861 +++++++ MonocleEngineDemo/Monocle/Util/BitTag.cs | 55 + MonocleEngineDemo/Monocle/Util/Cache.cs | 46 + MonocleEngineDemo/Monocle/Util/Calc.cs | 2198 +++++++++++++++++ MonocleEngineDemo/Monocle/Util/Camera.cs | 252 ++ .../Monocle/Util/CheatListener.cs | 75 + MonocleEngineDemo/Monocle/Util/ChoiceSet.cs | 172 ++ MonocleEngineDemo/Monocle/Util/Chooser.cs | 158 ++ MonocleEngineDemo/Monocle/Util/Commands.cs | 844 +++++++ MonocleEngineDemo/Monocle/Util/Draw.cs | 389 +++ MonocleEngineDemo/Monocle/Util/Ease.cs | 125 + MonocleEngineDemo/Monocle/Util/ErrorLog.cs | 78 + .../Monocle/Util/MethodHandle.cs | 23 + MonocleEngineDemo/Monocle/Util/PixelFont.cs | 430 ++++ MonocleEngineDemo/Monocle/Util/Pnt.cs | 118 + MonocleEngineDemo/Monocle/Util/Pooler.cs | 67 + MonocleEngineDemo/Monocle/Util/SaveLoad.cs | 150 ++ MonocleEngineDemo/Monocle/Util/SimpleCurve.cs | 78 + MonocleEngineDemo/Monocle/Util/SpecEntity.cs | 34 + MonocleEngineDemo/Monocle/Util/Tiler.cs | 275 +++ MonocleEngineDemo/Monocle/Util/Tracker.cs | 398 +++ MonocleEngineDemo/Monocle/Util/VirtualMap.cs | 121 + .../MonocleDemo/Content/Atlases/testboxes.xml | 18 + .../Content/Atlases/testboxes0.png | Bin 0 -> 426 bytes .../MonocleDemo/Content/Content.mgcb | 15 + .../MonocleDemo/Content/spriteData/Notes.txt | 62 + .../Content/spriteData/Sprites.xml | 18 + MonocleEngineDemo/MonocleDemo/FNA.dll | Bin 0 -> 941056 bytes .../GameEntities/Abstract/Dummy.cs | 49 + .../GameEntities/Abstract/SolidWall.cs | 29 + MonocleEngineDemo/MonocleDemo/Icon.bmp | Bin 0 -> 262282 bytes MonocleEngineDemo/MonocleDemo/Icon.ico | Bin 0 -> 147541 bytes .../MonocleDemo/Logic/GAccess.cs | 24 + MonocleEngineDemo/MonocleDemo/Logic/GFX.cs | 66 + MonocleEngineDemo/MonocleDemo/MonoDemo.cs | 101 + MonocleEngineDemo/MonocleDemo/Monocle.dll | Bin 0 -> 276480 bytes .../MonocleDemo/MonocleDemo.csproj | 171 ++ MonocleEngineDemo/MonocleDemo/Program.cs | 24 + .../MonocleDemo/Properties/AssemblyInfo.cs | 36 + .../MonocleDemo/Scenes/Menus/InitScene.cs | 130 + .../MonocleDemo/Scenes/Menus/TestScene.cs | 24 + MonocleEngineDemo/MonocleDemo/app.config | 3 + MonocleEngineDemo/MonocleDemo/app.manifest | 42 + MonocleEngineDemo/MonocleEngineDemo.sln | 31 + README.md | 4 + Sources/Atlases/testboxes.xml | 18 + Sources/Atlases/testboxes0.png | Bin 0 -> 426 bytes .../Graphics/testing/base_cubes/arrow0.png | Bin 0 -> 190 bytes .../Graphics/testing/base_cubes/arrow1.png | Bin 0 -> 191 bytes .../Graphics/testing/base_cubes/arrow2.png | Bin 0 -> 188 bytes .../Graphics/testing/base_cubes/arrow3.png | Bin 0 -> 185 bytes .../Graphics/testing/base_cubes/arrow4.png | Bin 0 -> 213 bytes .../Graphics/testing/base_cubes/arrow5.png | Bin 0 -> 236 bytes .../Graphics/testing/base_cubes/arrow6.png | Bin 0 -> 263 bytes .../Graphics/testing/base_cubes/arrow7.png | Bin 0 -> 275 bytes .../testing/base_cubes/black_base.png | Bin 0 -> 159 bytes .../Graphics/testing/base_cubes/blue_base.png | Bin 0 -> 160 bytes .../testing/base_cubes/green_base.png | Bin 0 -> 162 bytes .../Graphics/testing/base_cubes/red_base.png | Bin 0 -> 162 bytes .../testing/base_cubes/white_base.png | Bin 0 -> 151 bytes .../testing/base_cubes/yellow_base.png | Bin 0 -> 161 bytes Tools/Crunch/crunch.exe | Bin 0 -> 168960 bytes Tools/Crunch/readme.md | 120 + 122 files changed, 20538 insertions(+) create mode 100644 .gitignore create mode 100644 MonocleEngineDemo/Monocle/Colliders/Circle.cs create mode 100644 MonocleEngineDemo/Monocle/Colliders/Collide.cs create mode 100644 MonocleEngineDemo/Monocle/Colliders/Collider.cs create mode 100644 MonocleEngineDemo/Monocle/Colliders/ColliderList.cs create mode 100644 MonocleEngineDemo/Monocle/Colliders/Grid.cs create mode 100644 MonocleEngineDemo/Monocle/Colliders/Hitbox.cs create mode 100644 MonocleEngineDemo/Monocle/Components/CollidableComponent.cs create mode 100644 MonocleEngineDemo/Monocle/Components/Component.cs create mode 100644 MonocleEngineDemo/Monocle/Components/Graphics/GraphicsComponent.cs create mode 100644 MonocleEngineDemo/Monocle/Components/Graphics/Image.cs create mode 100644 MonocleEngineDemo/Monocle/Components/Graphics/ParticleEmitter.cs create mode 100644 MonocleEngineDemo/Monocle/Components/Graphics/PixelText.cs create mode 100644 MonocleEngineDemo/Monocle/Components/Graphics/Sprite.cs create mode 100644 MonocleEngineDemo/Monocle/Components/Graphics/Spritesheet.cs create mode 100644 MonocleEngineDemo/Monocle/Components/Graphics/Text/NumberText.cs create mode 100644 MonocleEngineDemo/Monocle/Components/Graphics/Text/OutlineText.cs create mode 100644 MonocleEngineDemo/Monocle/Components/Graphics/Text/Text.cs create mode 100644 MonocleEngineDemo/Monocle/Components/Graphics/Text/TimerText.cs create mode 100644 MonocleEngineDemo/Monocle/Components/Graphics/TileGrid.cs create mode 100644 MonocleEngineDemo/Monocle/Components/Logic/Alarm.cs create mode 100644 MonocleEngineDemo/Monocle/Components/Logic/Coroutine.cs create mode 100644 MonocleEngineDemo/Monocle/Components/Logic/CoroutineHolder.cs create mode 100644 MonocleEngineDemo/Monocle/Components/Logic/CounterSet.cs create mode 100644 MonocleEngineDemo/Monocle/Components/Logic/Shaker.cs create mode 100644 MonocleEngineDemo/Monocle/Components/Logic/ShakerList.cs create mode 100644 MonocleEngineDemo/Monocle/Components/Logic/SineWave.cs create mode 100644 MonocleEngineDemo/Monocle/Components/Logic/StateMachine.cs create mode 100644 MonocleEngineDemo/Monocle/Components/Logic/Tween.cs create mode 100644 MonocleEngineDemo/Monocle/Components/Logic/Wiggler.cs create mode 100644 MonocleEngineDemo/Monocle/Content/Monocle/MonocleDefault.spritefont create mode 100644 MonocleEngineDemo/Monocle/Content/Monocle/MonocleDefault.xnb create mode 100644 MonocleEngineDemo/Monocle/Engine.cs create mode 100644 MonocleEngineDemo/Monocle/Entity.cs create mode 100644 MonocleEngineDemo/Monocle/Graphics/Atlas.cs create mode 100644 MonocleEngineDemo/Monocle/Graphics/MTexture.cs create mode 100644 MonocleEngineDemo/Monocle/Graphics/SpriteBank.cs create mode 100644 MonocleEngineDemo/Monocle/Graphics/SpriteData.cs create mode 100644 MonocleEngineDemo/Monocle/Graphics/Tileset.cs create mode 100644 MonocleEngineDemo/Monocle/Input/MInput.cs create mode 100644 MonocleEngineDemo/Monocle/Input/VirtualAxis.cs create mode 100644 MonocleEngineDemo/Monocle/Input/VirtualButton.cs create mode 100644 MonocleEngineDemo/Monocle/Input/VirtualInput.cs create mode 100644 MonocleEngineDemo/Monocle/Input/VirtualIntegerAxis.cs create mode 100644 MonocleEngineDemo/Monocle/Input/VirtualJoystick.cs create mode 100644 MonocleEngineDemo/Monocle/InternalUtilities/ComponentList.cs create mode 100644 MonocleEngineDemo/Monocle/InternalUtilities/EntityList.cs create mode 100644 MonocleEngineDemo/Monocle/InternalUtilities/RendererList.cs create mode 100644 MonocleEngineDemo/Monocle/InternalUtilities/TagLists.cs create mode 100644 MonocleEngineDemo/Monocle/Monocle.MonoGame.csproj create mode 100644 MonocleEngineDemo/Monocle/Monocle.csproj create mode 100644 MonocleEngineDemo/Monocle/Particles/Particle.cs create mode 100644 MonocleEngineDemo/Monocle/Particles/ParticleSystem.cs create mode 100644 MonocleEngineDemo/Monocle/Particles/ParticleType.cs create mode 100644 MonocleEngineDemo/Monocle/Properties/AssemblyInfo.cs create mode 100644 MonocleEngineDemo/Monocle/Renderers/EverythingRenderer.cs create mode 100644 MonocleEngineDemo/Monocle/Renderers/Renderer.cs create mode 100644 MonocleEngineDemo/Monocle/Renderers/SingleTagRenderer.cs create mode 100644 MonocleEngineDemo/Monocle/Renderers/TagExcludeRenderer.cs create mode 100644 MonocleEngineDemo/Monocle/Scene.cs create mode 100644 MonocleEngineDemo/Monocle/Util/BitTag.cs create mode 100644 MonocleEngineDemo/Monocle/Util/Cache.cs create mode 100644 MonocleEngineDemo/Monocle/Util/Calc.cs create mode 100644 MonocleEngineDemo/Monocle/Util/Camera.cs create mode 100644 MonocleEngineDemo/Monocle/Util/CheatListener.cs create mode 100644 MonocleEngineDemo/Monocle/Util/ChoiceSet.cs create mode 100644 MonocleEngineDemo/Monocle/Util/Chooser.cs create mode 100644 MonocleEngineDemo/Monocle/Util/Commands.cs create mode 100644 MonocleEngineDemo/Monocle/Util/Draw.cs create mode 100644 MonocleEngineDemo/Monocle/Util/Ease.cs create mode 100644 MonocleEngineDemo/Monocle/Util/ErrorLog.cs create mode 100644 MonocleEngineDemo/Monocle/Util/MethodHandle.cs create mode 100644 MonocleEngineDemo/Monocle/Util/PixelFont.cs create mode 100644 MonocleEngineDemo/Monocle/Util/Pnt.cs create mode 100644 MonocleEngineDemo/Monocle/Util/Pooler.cs create mode 100644 MonocleEngineDemo/Monocle/Util/SaveLoad.cs create mode 100644 MonocleEngineDemo/Monocle/Util/SimpleCurve.cs create mode 100644 MonocleEngineDemo/Monocle/Util/SpecEntity.cs create mode 100644 MonocleEngineDemo/Monocle/Util/Tiler.cs create mode 100644 MonocleEngineDemo/Monocle/Util/Tracker.cs create mode 100644 MonocleEngineDemo/Monocle/Util/VirtualMap.cs create mode 100644 MonocleEngineDemo/MonocleDemo/Content/Atlases/testboxes.xml create mode 100644 MonocleEngineDemo/MonocleDemo/Content/Atlases/testboxes0.png create mode 100644 MonocleEngineDemo/MonocleDemo/Content/Content.mgcb create mode 100644 MonocleEngineDemo/MonocleDemo/Content/spriteData/Notes.txt create mode 100644 MonocleEngineDemo/MonocleDemo/Content/spriteData/Sprites.xml create mode 100644 MonocleEngineDemo/MonocleDemo/FNA.dll create mode 100644 MonocleEngineDemo/MonocleDemo/GameEntities/Abstract/Dummy.cs create mode 100644 MonocleEngineDemo/MonocleDemo/GameEntities/Abstract/SolidWall.cs create mode 100644 MonocleEngineDemo/MonocleDemo/Icon.bmp create mode 100644 MonocleEngineDemo/MonocleDemo/Icon.ico create mode 100644 MonocleEngineDemo/MonocleDemo/Logic/GAccess.cs create mode 100644 MonocleEngineDemo/MonocleDemo/Logic/GFX.cs create mode 100644 MonocleEngineDemo/MonocleDemo/MonoDemo.cs create mode 100644 MonocleEngineDemo/MonocleDemo/Monocle.dll create mode 100644 MonocleEngineDemo/MonocleDemo/MonocleDemo.csproj create mode 100644 MonocleEngineDemo/MonocleDemo/Program.cs create mode 100644 MonocleEngineDemo/MonocleDemo/Properties/AssemblyInfo.cs create mode 100644 MonocleEngineDemo/MonocleDemo/Scenes/Menus/InitScene.cs create mode 100644 MonocleEngineDemo/MonocleDemo/Scenes/Menus/TestScene.cs create mode 100644 MonocleEngineDemo/MonocleDemo/app.config create mode 100644 MonocleEngineDemo/MonocleDemo/app.manifest create mode 100644 MonocleEngineDemo/MonocleEngineDemo.sln create mode 100644 Sources/Atlases/testboxes.xml create mode 100644 Sources/Atlases/testboxes0.png create mode 100644 Sources/Graphics/testing/base_cubes/arrow0.png create mode 100644 Sources/Graphics/testing/base_cubes/arrow1.png create mode 100644 Sources/Graphics/testing/base_cubes/arrow2.png create mode 100644 Sources/Graphics/testing/base_cubes/arrow3.png create mode 100644 Sources/Graphics/testing/base_cubes/arrow4.png create mode 100644 Sources/Graphics/testing/base_cubes/arrow5.png create mode 100644 Sources/Graphics/testing/base_cubes/arrow6.png create mode 100644 Sources/Graphics/testing/base_cubes/arrow7.png create mode 100644 Sources/Graphics/testing/base_cubes/black_base.png create mode 100644 Sources/Graphics/testing/base_cubes/blue_base.png create mode 100644 Sources/Graphics/testing/base_cubes/green_base.png create mode 100644 Sources/Graphics/testing/base_cubes/red_base.png create mode 100644 Sources/Graphics/testing/base_cubes/white_base.png create mode 100644 Sources/Graphics/testing/base_cubes/yellow_base.png create mode 100644 Tools/Crunch/crunch.exe create mode 100644 Tools/Crunch/readme.md diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6d3a38c --- /dev/null +++ b/.gitignore @@ -0,0 +1,341 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ +# ASP.NET Core default setup: bower directory is configured as wwwroot/lib/ and bower restore is true +**/wwwroot/lib/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# JetBrains Rider +.idea/ +*.sln.iml + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb \ No newline at end of file diff --git a/MonocleEngineDemo/Monocle/Colliders/Circle.cs b/MonocleEngineDemo/Monocle/Colliders/Circle.cs new file mode 100644 index 0000000..f0c2361 --- /dev/null +++ b/MonocleEngineDemo/Monocle/Colliders/Circle.cs @@ -0,0 +1,103 @@ +using Microsoft.Xna.Framework; +using System; + +namespace Monocle +{ + public class Circle : Collider + { + public float Radius; + + public Circle(float radius, float x = 0, float y = 0) + { + Radius = radius; + Position.X = x; + Position.Y = y; + } + + public override float Width + { + get { return Radius * 2; } + set { Radius = value / 2; } + } + + public override float Height + { + get { return Radius * 2; } + set { Radius = value / 2; } + } + + public override float Left + { + get { return Position.X - Radius; } + set { Position.X = value + Radius; } + } + + public override float Top + { + get { return Position.Y - Radius; } + set { Position.Y = value + Radius; } + } + + public override float Right + { + get { return Position.X + Radius; } + set { Position.X = value - Radius; } + } + + public override float Bottom + { + get { return Position.Y + Radius; } + set { Position.Y = value - Radius; } + } + + public override Collider Clone() + { + return new Circle(Radius, Position.X, Position.Y); + } + + public override void Render(Camera camera, Color color) + { + Draw.Circle(AbsolutePosition, Radius, color, 4); + } + + /* + * Checking against other colliders + */ + + public override bool Collide(Vector2 point) + { + return Monocle.Collide.CircleToPoint(AbsolutePosition, Radius, point); + } + + public override bool Collide(Rectangle rect) + { + return Monocle.Collide.RectToCircle(rect, AbsolutePosition, Radius); + } + + public override bool Collide(Vector2 from, Vector2 to) + { + return Monocle.Collide.CircleToLine(AbsolutePosition, Radius, from, to); + } + + public override bool Collide(Circle circle) + { + return Vector2.DistanceSquared(AbsolutePosition, circle.AbsolutePosition) < (Radius + circle.Radius) * (Radius + circle.Radius); + } + + public override bool Collide(Hitbox hitbox) + { + return hitbox.Collide(this); + } + + public override bool Collide(Grid grid) + { + return grid.Collide(this); + } + + public override bool Collide(ColliderList list) + { + return list.Collide(this); + } + + } +} diff --git a/MonocleEngineDemo/Monocle/Colliders/Collide.cs b/MonocleEngineDemo/Monocle/Colliders/Collide.cs new file mode 100644 index 0000000..3d04a17 --- /dev/null +++ b/MonocleEngineDemo/Monocle/Colliders/Collide.cs @@ -0,0 +1,440 @@ +using Microsoft.Xna.Framework; +using System; +using System.Collections.Generic; + +namespace Monocle +{ + [Flags] + public enum PointSectors { Center = 0, Top = 1, Bottom = 2, TopLeft = 9, TopRight = 5, Left = 8, Right = 4, BottomLeft = 10, BottomRight = 6 }; + + public static class Collide + { + #region Entity vs Entity + + public static bool Check(Entity a, Entity b) + { + if (a.Collider == null || b.Collider == null) + return false; + else + return a != b && b.Collidable && a.Collider.Collide(b); + } + + public static bool Check(Entity a, Entity b, Vector2 at) + { + Vector2 old = a.Position; + a.Position = at; + bool ret = Check(a, b); + a.Position = old; + return ret; + } + + public static bool Check(Entity a, CollidableComponent b) + { + if (a.Collider == null || b.Collider == null) + return false; + else + return b.Collidable && b.Entity.Collidable && a.Collider.Collide(b); + } + + public static bool Check(Entity a, CollidableComponent b, Vector2 at) + { + Vector2 old = a.Position; + a.Position = at; + bool ret = Check(a, b); + a.Position = old; + return ret; + } + + #endregion + + #region Entity vs Entity Enumerable + + #region Check + + public static bool Check(Entity a, IEnumerable b) + { + foreach (var e in b) + if (Check(a, e)) + return true; + + return false; + } + + public static bool Check(Entity a, IEnumerable b, Vector2 at) + { + Vector2 old = a.Position; + a.Position = at; + bool ret = Check(a, b); + a.Position = old; + return ret; + } + + #endregion + + #region First + + public static Entity First(Entity a, IEnumerable b) + { + foreach (var e in b) + if (Check(a, e)) + return e; + + return null; + } + + public static Entity First(Entity a, IEnumerable b, Vector2 at) + { + Vector2 old = a.Position; + a.Position = at; + Entity ret = First(a, b); + a.Position = old; + return ret; + } + + #endregion + + #region All + + public static List All(Entity a, IEnumerable b, List into) + { + foreach (var e in b) + if (Check(a, e)) + into.Add(e); + + return into; + } + + public static List All(Entity a, IEnumerable b, List into, Vector2 at) + { + Vector2 old = a.Position; + a.Position = at; + List ret = All(a, b, into); + a.Position = old; + return ret; + } + + public static List All(Entity a, IEnumerable b) + { + return All(a, b, new List()); + } + + public static List All(Entity a, IEnumerable b, Vector2 at) + { + return All(a, b, new List(), at); + } + + #endregion + + #endregion + + #region Entity vs Point + + public static bool CheckPoint(Entity a, Vector2 point) + { + if (a.Collider == null) + return false; + else + return a.Collider.Collide(point); + } + + public static bool CheckPoint(Entity a, Vector2 point, Vector2 at) + { + Vector2 old = a.Position; + a.Position = at; + bool ret = CheckPoint(a, point); + a.Position = old; + return ret; + } + + #endregion + + #region Entity vs Line + + public static bool CheckLine(Entity a, Vector2 from, Vector2 to) + { + if (a.Collider == null) + return false; + else + return a.Collider.Collide(from, to); + } + + public static bool CheckLine(Entity a, Vector2 from, Vector2 to, Vector2 at) + { + Vector2 old = a.Position; + a.Position = at; + bool ret = CheckLine(a, from, to); + a.Position = old; + return ret; + } + + #endregion + + #region Entity vs Rectangle + + public static bool CheckRect(Entity a, Rectangle rect) + { + if (a.Collider == null) + return false; + else + return a.Collider.Collide(rect); + } + + public static bool CheckRect(Entity a, Rectangle rect, Vector2 at) + { + Vector2 old = a.Position; + a.Position = at; + bool ret = CheckRect(a, rect); + a.Position = old; + return ret; + } + + #endregion + + #region Line + + public static bool LineCheck(Vector2 a1, Vector2 a2, Vector2 b1, Vector2 b2) + { + Vector2 b = a2 - a1; + Vector2 d = b2 - b1; + float bDotDPerp = b.X * d.Y - b.Y * d.X; + + // if b dot d == 0, it means the lines are parallel so have infinite intersection points + if (bDotDPerp == 0) + return false; + + Vector2 c = b1 - a1; + float t = (c.X * d.Y - c.Y * d.X) / bDotDPerp; + if (t < 0 || t > 1) + return false; + + float u = (c.X * b.Y - c.Y * b.X) / bDotDPerp; + if (u < 0 || u > 1) + return false; + + return true; + } + + public static bool LineCheck(Vector2 a1, Vector2 a2, Vector2 b1, Vector2 b2, out Vector2 intersection) + { + intersection = Vector2.Zero; + + Vector2 b = a2 - a1; + Vector2 d = b2 - b1; + float bDotDPerp = b.X * d.Y - b.Y * d.X; + + // if b dot d == 0, it means the lines are parallel so have infinite intersection points + if (bDotDPerp == 0) + return false; + + Vector2 c = b1 - a1; + float t = (c.X * d.Y - c.Y * d.X) / bDotDPerp; + if (t < 0 || t > 1) + return false; + + float u = (c.X * b.Y - c.Y * b.X) / bDotDPerp; + if (u < 0 || u > 1) + return false; + + intersection = a1 + t * b; + + return true; + } + + #endregion + + #region Circle + + public static bool CircleToLine(Vector2 cPosiition, float cRadius, Vector2 lineFrom, Vector2 lineTo) + { + return Vector2.DistanceSquared(cPosiition, Calc.ClosestPointOnLine(lineFrom, lineTo, cPosiition)) < cRadius * cRadius; + } + + public static bool CircleToPoint(Vector2 cPosition, float cRadius, Vector2 point) + { + return Vector2.DistanceSquared(cPosition, point) < cRadius * cRadius; + } + + public static bool CircleToRect(Vector2 cPosition, float cRadius, float rX, float rY, float rW, float rH) + { + return RectToCircle(rX, rY, rW, rH, cPosition, cRadius); + } + + public static bool CircleToRect(Vector2 cPosition, float cRadius, Rectangle rect) + { + return RectToCircle(rect, cPosition, cRadius); + } + + #endregion + + #region Rect + + public static bool RectToCircle(float rX, float rY, float rW, float rH, Vector2 cPosition, float cRadius) + { + //Check if the rectangle contains the circle's center point + if (Collide.RectToPoint(rX, rY, rW, rH, cPosition)) + return true; + + //Check the circle against the relevant edges + Vector2 edgeFrom; + Vector2 edgeTo; + PointSectors sector = GetSector(rX, rY, rW, rH, cPosition); + + if ((sector & PointSectors.Top) != 0) + { + edgeFrom = new Vector2(rX, rY); + edgeTo = new Vector2(rX + rW, rY); + if (CircleToLine(cPosition, cRadius, edgeFrom, edgeTo)) + return true; + } + + if ((sector & PointSectors.Bottom) != 0) + { + edgeFrom = new Vector2(rX, rY + rH); + edgeTo = new Vector2(rX + rW, rY + rH); + if (CircleToLine(cPosition, cRadius, edgeFrom, edgeTo)) + return true; + } + + if ((sector & PointSectors.Left) != 0) + { + edgeFrom = new Vector2(rX, rY); + edgeTo = new Vector2(rX, rY + rH); + if (CircleToLine(cPosition, cRadius, edgeFrom, edgeTo)) + return true; + } + + if ((sector & PointSectors.Right) != 0) + { + edgeFrom = new Vector2(rX + rW, rY); + edgeTo = new Vector2(rX + rW, rY + rH); + if (CircleToLine(cPosition, cRadius, edgeFrom, edgeTo)) + return true; + } + + return false; + } + + public static bool RectToCircle(Rectangle rect, Vector2 cPosition, float cRadius) + { + return RectToCircle(rect.X, rect.Y, rect.Width, rect.Height, cPosition, cRadius); + } + + public static bool RectToLine(float rX, float rY, float rW, float rH, Vector2 lineFrom, Vector2 lineTo) + { + PointSectors fromSector = Monocle.Collide.GetSector(rX, rY, rW, rH, lineFrom); + PointSectors toSector = Monocle.Collide.GetSector(rX, rY, rW, rH, lineTo); + + if (fromSector == PointSectors.Center || toSector == PointSectors.Center) + return true; + else if ((fromSector & toSector) != 0) + return false; + else + { + PointSectors both = fromSector | toSector; + + //Do line checks against the edges + Vector2 edgeFrom; + Vector2 edgeTo; + + if ((both & PointSectors.Top) != 0) + { + edgeFrom = new Vector2(rX, rY); + edgeTo = new Vector2(rX + rW, rY); + if (Monocle.Collide.LineCheck(edgeFrom, edgeTo, lineFrom, lineTo)) + return true; + } + + if ((both & PointSectors.Bottom) != 0) + { + edgeFrom = new Vector2(rX, rY + rH); + edgeTo = new Vector2(rX + rW, rY + rH); + if (Monocle.Collide.LineCheck(edgeFrom, edgeTo, lineFrom, lineTo)) + return true; + } + + if ((both & PointSectors.Left) != 0) + { + edgeFrom = new Vector2(rX, rY); + edgeTo = new Vector2(rX, rY + rH); + if (Monocle.Collide.LineCheck(edgeFrom, edgeTo, lineFrom, lineTo)) + return true; + } + + if ((both & PointSectors.Right) != 0) + { + edgeFrom = new Vector2(rX + rW, rY); + edgeTo = new Vector2(rX + rW, rY + rH); + if (Monocle.Collide.LineCheck(edgeFrom, edgeTo, lineFrom, lineTo)) + return true; + } + } + + return false; + } + + public static bool RectToLine(Rectangle rect, Vector2 lineFrom, Vector2 lineTo) + { + return RectToLine(rect.X, rect.Y, rect.Width, rect.Height, lineFrom, lineTo); + } + + public static bool RectToPoint(float rX, float rY, float rW, float rH, Vector2 point) + { + return point.X >= rX && point.Y >= rY && point.X < rX + rW && point.Y < rY + rH; + } + + public static bool RectToPoint(Rectangle rect, Vector2 point) + { + return RectToPoint(rect.X, rect.Y, rect.Width, rect.Height, point); + } + + #endregion + + #region Sectors + + /* + * Bitflags and helpers for using the Cohen–Sutherland algorithm + * http://en.wikipedia.org/wiki/Cohen%E2%80%93Sutherland_algorithm + * + * Sector bitflags: + * 1001 1000 1010 + * 0001 0000 0010 + * 0101 0100 0110 + */ + + public static PointSectors GetSector(Rectangle rect, Vector2 point) + { + PointSectors sector = PointSectors.Center; + + if (point.X < rect.Left) + sector |= PointSectors.Left; + else if (point.X >= rect.Right) + sector |= PointSectors.Right; + + if (point.Y < rect.Top) + sector |= PointSectors.Top; + else if (point.Y >= rect.Bottom) + sector |= PointSectors.Bottom; + + return sector; + } + + public static PointSectors GetSector(float rX, float rY, float rW, float rH, Vector2 point) + { + PointSectors sector = PointSectors.Center; + + if (point.X < rX) + sector |= PointSectors.Left; + else if (point.X >= rX + rW) + sector |= PointSectors.Right; + + if (point.Y < rY) + sector |= PointSectors.Top; + else if (point.Y >= rY + rH) + sector |= PointSectors.Bottom; + + return sector; + } + + #endregion + } +} diff --git a/MonocleEngineDemo/Monocle/Colliders/Collider.cs b/MonocleEngineDemo/Monocle/Colliders/Collider.cs new file mode 100644 index 0000000..b94715e --- /dev/null +++ b/MonocleEngineDemo/Monocle/Colliders/Collider.cs @@ -0,0 +1,342 @@ +using Microsoft.Xna.Framework; +using System; + +namespace Monocle +{ + public abstract class Collider + { + public Entity Entity { get; internal set; } + public Component Component { get; private set; } + public Vector2 Position; + + internal virtual void Added(Entity entity) + { + Entity = entity; + Component = null; + } + + internal virtual void Added(Component component) + { + Entity = component.Entity; + Component = component; + } + + internal virtual void Removed() + { + Entity = null; + Component = null; + } + + public bool Collide(Entity entity) + { + return Collide(entity.Collider); + } + + public bool Collide(CollidableComponent component) + { + return Collide(component.Collider); + } + + public bool Collide(Collider collider) + { + if (collider is Hitbox) + { + return Collide(collider as Hitbox); + } + else if (collider is Grid) + { + return Collide(collider as Grid); + } + else if (collider is ColliderList) + { + return Collide(collider as ColliderList); + } + else if (collider is Circle) + { + return Collide(collider as Circle); + } + else + throw new Exception("Collisions against the collider type are not implemented!"); + } + + public abstract bool Collide(Vector2 point); + public abstract bool Collide(Rectangle rect); + public abstract bool Collide(Vector2 from, Vector2 to); + public abstract bool Collide(Hitbox hitbox); + public abstract bool Collide(Grid grid); + public abstract bool Collide(Circle circle); + public abstract bool Collide(ColliderList list); + public abstract Collider Clone(); + public abstract void Render(Camera camera, Color color); + public abstract float Width { get; set; } + public abstract float Height { get; set; } + public abstract float Top { get; set; } + public abstract float Bottom { get; set; } + public abstract float Left { get; set; } + public abstract float Right { get; set; } + + public void CenterOrigin() + { + Position.X = -Width / 2; + Position.Y = -Height / 2; + } + + public float CenterX + { + get + { + return Left + Width / 2f; + } + + set + { + Left = value - Width / 2f; + } + } + + public float CenterY + { + get + { + return Top + Height / 2f; + } + + set + { + Top = value - Height / 2f; + } + } + + public Vector2 TopLeft + { + get + { + return new Vector2(Left, Top); + } + + set + { + Left = value.X; + Top = value.Y; + } + } + + public Vector2 TopCenter + { + get + { + return new Vector2(CenterX, Top); + } + + set + { + CenterX = value.X; + Top = value.Y; + } + } + + public Vector2 TopRight + { + get + { + return new Vector2(Right, Top); + } + + set + { + Right = value.X; + Top = value.Y; + } + } + + public Vector2 CenterLeft + { + get + { + return new Vector2(Left, CenterY); + } + + set + { + Left = value.X; + CenterY = value.Y; + } + } + + public Vector2 Center + { + get + { + return new Vector2(CenterX, CenterY); + } + + set + { + CenterX = value.X; + CenterY = value.Y; + } + } + + public Vector2 Size + { + get + { + return new Vector2(Width, Height); + } + } + + public Vector2 HalfSize + { + get + { + return Size * .5f; + } + } + + public Vector2 CenterRight + { + get + { + return new Vector2(Right, CenterY); + } + + set + { + Right = value.X; + CenterY = value.Y; + } + } + + public Vector2 BottomLeft + { + get + { + return new Vector2(Left, Bottom); + } + + set + { + Left = value.X; + Bottom = value.Y; + } + } + + public Vector2 BottomCenter + { + get + { + return new Vector2(CenterX, Bottom); + } + + set + { + CenterX = value.X; + Bottom = value.Y; + } + } + + public Vector2 BottomRight + { + get + { + return new Vector2(Right, Bottom); + } + + set + { + Right = value.X; + Bottom = value.Y; + } + } + + public void Render(Camera camera) + { + Render(camera, Color.Red); + } + + public Vector2 AbsolutePosition + { + get + { + if (Entity != null) + return Entity.Position + Position; + else + return Position; + } + } + + public float AbsoluteX + { + get + { + if (Entity != null) + return Entity.Position.X + Position.X; + else + return Position.X; + } + } + + public float AbsoluteY + { + get + { + if (Entity != null) + return Entity.Position.Y + Position.Y; + else + return Position.Y; + } + } + + public float AbsoluteTop + { + get + { + if (Entity != null) + return Top + Entity.Position.Y; + else + return Top; + } + } + + public float AbsoluteBottom + { + get + { + if (Entity != null) + return Bottom + Entity.Position.Y; + else + return Bottom; + } + } + + public float AbsoluteLeft + { + get + { + if (Entity != null) + return Left + Entity.Position.X; + else + return Left; + } + } + + public float AbsoluteRight + { + get + { + if (Entity != null) + return Right + Entity.Position.X; + else + return Right; + } + } + + public Rectangle Bounds + { + get + { + return new Rectangle((int)AbsoluteLeft, (int)AbsoluteTop, (int)Width, (int)Height); + } + } + } +} diff --git a/MonocleEngineDemo/Monocle/Colliders/ColliderList.cs b/MonocleEngineDemo/Monocle/Colliders/ColliderList.cs new file mode 100644 index 0000000..d772c5c --- /dev/null +++ b/MonocleEngineDemo/Monocle/Colliders/ColliderList.cs @@ -0,0 +1,266 @@ +using Microsoft.Xna.Framework; +using System; +using System.Linq; + +namespace Monocle +{ + public class ColliderList : Collider + { + public Collider[] colliders { get; private set; } + + public ColliderList(params Collider[] colliders) + { +#if DEBUG + foreach (var c in colliders) + if (c == null) + throw new Exception("Cannot add a null Collider to a ColliderList."); +#endif + this.colliders = colliders; + } + + public void Add(params Collider[] toAdd) + { +#if DEBUG + foreach (var c in toAdd) + { + if (colliders.Contains(c)) + throw new Exception("Adding a Collider to a ColliderList that already contains it!"); + else if (c == null) + throw new Exception("Cannot add a null Collider to a ColliderList."); + } +#endif + + Collider[] newColliders = new Collider[colliders.Length + toAdd.Length]; + for (int i = 0; i < colliders.Length; i++) + newColliders[i] = colliders[i]; + for (int i = 0; i < toAdd.Length; i++) + { + newColliders[i + colliders.Length] = toAdd[i]; + toAdd[i].Added(Entity); + } + colliders = newColliders; + } + + public void Remove(params Collider[] toRemove) + { +#if DEBUG + foreach (var c in toRemove) + { + if (!colliders.Contains(c)) + throw new Exception("Removing a Collider from a ColliderList that does not contain it!"); + else if (c == null) + throw new Exception("Cannot remove a null Collider from a ColliderList."); + } +#endif + + Collider[] newColliders = new Collider[colliders.Length - toRemove.Length]; + int at = 0; + foreach (var c in colliders) + { + if (!toRemove.Contains(c)) + { + newColliders[at] = c; + at++; + } + } + colliders = newColliders; + } + + internal override void Added(Entity entity) + { + base.Added(entity); + foreach (var c in colliders) + c.Added(entity); + } + + internal override void Removed() + { + base.Removed(); + foreach (var c in colliders) + c.Removed(); + } + + public override float Width + { + get + { + return Right - Left; + } + + set + { + throw new NotImplementedException(); + } + } + + public override float Height + { + get + { + return Bottom - Top; + } + set + { + throw new NotImplementedException(); + } + } + + public override float Left + { + get + { + float left = colliders[0].Left; + for (int i = 1; i < colliders.Length; i++) + if (colliders[i].Left < left) + left = colliders[i].Left; + return left; + } + + set + { + float changeX = value - Left; + foreach (var c in colliders) + Position.X += changeX; + } + } + + public override float Right + { + get + { + float right = colliders[0].Right; + for (int i = 1; i < colliders.Length; i++) + if (colliders[i].Right > right) + right = colliders[i].Right; + return right; + } + + set + { + float changeX = value - Right; + foreach (var c in colliders) + Position.X += changeX; + } + } + + public override float Top + { + get + { + float top = colliders[0].Top; + for (int i = 1; i < colliders.Length; i++) + if (colliders[i].Top < top) + top = colliders[i].Top; + return top; + } + + set + { + float changeY = value - Top; + foreach (var c in colliders) + Position.Y += changeY; + } + } + + public override float Bottom + { + get + { + float bottom = colliders[0].Bottom; + for (int i = 1; i < colliders.Length; i++) + if (colliders[i].Bottom > bottom) + bottom = colliders[i].Bottom; + return bottom; + } + + set + { + float changeY = value - Bottom; + foreach (var c in colliders) + Position.Y += changeY; + } + } + + public override Collider Clone() + { + Collider[] clones = new Collider[colliders.Length]; + for (int i = 0; i < colliders.Length; i++) + clones[i] = colliders[i].Clone(); + + return new ColliderList(clones); + } + + public override void Render(Camera camera, Color color) + { + foreach (var c in colliders) + c.Render(camera, color); + } + + /* + * Checking against other colliders + */ + + public override bool Collide(Vector2 point) + { + foreach (var c in colliders) + if (c.Collide(point)) + return true; + + return false; + } + + public override bool Collide(Rectangle rect) + { + foreach (var c in colliders) + if (c.Collide(rect)) + return true; + + return false; + } + + public override bool Collide(Vector2 from, Vector2 to) + { + foreach (var c in colliders) + if (c.Collide(from, to)) + return true; + + return false; + } + + public override bool Collide(Hitbox hitbox) + { + foreach (var c in colliders) + if (c.Collide(hitbox)) + return true; + + return false; + } + + public override bool Collide(Grid grid) + { + foreach (var c in colliders) + if (c.Collide(grid)) + return true; + + return false; + } + + public override bool Collide(Circle circle) + { + foreach (var c in colliders) + if (c.Collide(circle)) + return true; + + return false; + } + + public override bool Collide(ColliderList list) + { + foreach (var c in colliders) + if (c.Collide(list)) + return true; + + return false; + } + } +} diff --git a/MonocleEngineDemo/Monocle/Colliders/Grid.cs b/MonocleEngineDemo/Monocle/Colliders/Grid.cs new file mode 100644 index 0000000..f7df847 --- /dev/null +++ b/MonocleEngineDemo/Monocle/Colliders/Grid.cs @@ -0,0 +1,489 @@ +using Microsoft.Xna.Framework; +using System; + +namespace Monocle +{ + public class Grid : Collider + { + public VirtualMap Data; + + public float CellWidth { get; private set; } + public float CellHeight { get; private set; } + + public Grid(int cellsX, int cellsY, float cellWidth, float cellHeight) + { + Data = new VirtualMap(cellsX, cellsY); + + CellWidth = cellWidth; + CellHeight = cellHeight; + } + + public Grid(float cellWidth, float cellHeight, string bitstring) + { + CellWidth = cellWidth; + CellHeight = cellHeight; + + //Find minimal size from bitstring + int longest = 0; + int currentX = 0; + int currentY = 1; + for (int i = 0; i < bitstring.Length; i++) + { + if (bitstring[i] == '\n') + { + currentY++; + longest = Math.Max(currentX, longest); + currentX = 0; + } + else + currentX++; + } + + Data = new VirtualMap(longest, currentY); + LoadBitstring(bitstring); + } + + public Grid(float cellWidth, float cellHeight, bool[,] data) + { + CellWidth = cellWidth; + CellHeight = cellHeight; + + this.Data = new VirtualMap(data); + } + + public Grid(float cellWidth, float cellHeight, VirtualMap data) + { + CellWidth = cellWidth; + CellHeight = cellHeight; + + this.Data = data; + } + + public void Extend(int left, int right, int up, int down) + { + Position -= new Vector2(left * CellWidth, up * CellHeight); + + int newWidth = Data.Columns + left + right; + int newHeight = Data.Rows + up + down; + if (newWidth <= 0 || newHeight <= 0) + { + Data = new VirtualMap(0, 0); + return; + } + + var newData = new VirtualMap(newWidth, newHeight); + + //Center + for (int x = 0; x < Data.Columns; x++) + { + for (int y = 0; y < Data.Rows; y++) + { + int atX = x + left; + int atY = y + up; + + if (atX >= 0 && atX < newWidth && atY >= 0 && atY < newHeight) + newData[atX, atY] = Data[x, y]; + } + } + + //Left + for (int x = 0; x < left; x++) + for (int y = 0; y < newHeight; y++) + newData[x, y] = Data[0, Calc.Clamp(y - up, 0, Data.Rows - 1)]; + + //Right + for (int x = newWidth - right; x < newWidth; x++) + for (int y = 0; y < newHeight; y++) + newData[x, y] = Data[Data.Columns - 1, Calc.Clamp(y - up, 0, Data.Rows - 1)]; + + //Top + for (int y = 0; y < up; y++) + for (int x = 0; x < newWidth; x++) + newData[x, y] = Data[Calc.Clamp(x - left, 0, Data.Columns - 1), 0]; + + //Bottom + for (int y = newHeight - down; y < newHeight; y++) + for (int x = 0; x < newWidth; x++) + newData[x, y] = Data[Calc.Clamp(x - left, 0, Data.Columns - 1), Data.Rows - 1]; + + Data = newData; + } + + public void LoadBitstring(string bitstring) + { + int x = 0; + int y = 0; + + for (int i = 0; i < bitstring.Length; i++) + { + if (bitstring[i] == '\n') + { + while (x < CellsX) + { + Data[x, y] = false; + x++; + } + + x = 0; + y++; + + if (y >= CellsY) + return; + } + else if (x < CellsX) + { + if (bitstring[i] == '0') + { + Data[x, y] = false; + x++; + } + else + { + Data[x, y] = true; + x++; + } + } + } + } + + public string GetBitstring() + { + string bits = ""; + for (int y = 0; y < CellsY; y++) + { + if (y != 0) + bits += "\n"; + + for (int x = 0; x < CellsX; x++) + { + if (Data[x, y]) + bits += "1"; + else + bits += "0"; + } + } + + return bits; + } + + public void Clear(bool to = false) + { + for (int i = 0; i < CellsX; i++) + for (int j = 0; j < CellsY; j++) + Data[i, j] = to; + } + + public void SetRect(int x, int y, int width, int height, bool to = true) + { + if (x < 0) + { + width += x; + x = 0; + } + + if (y < 0) + { + height += y; + y = 0; + } + + if (x + width > CellsX) + width = CellsX - x; + + if (y + height > CellsY) + height = CellsY - y; + + for (int i = 0; i < width; i++) + for (int j = 0; j < height; j++) + Data[x + i, y + j] = to; + } + + public bool CheckRect(int x, int y, int width, int height) + { + if (x < 0) + { + width += x; + x = 0; + } + + if (y < 0) + { + height += y; + y = 0; + } + + if (x + width > CellsX) + width = CellsX - x; + + if (y + height > CellsY) + height = CellsY - y; + + for (int i = 0; i < width; i++) + { + for (int j = 0; j < height; j++) + { + if (Data[x + i, y + j]) + return true; + } + } + + return false; + } + + public bool CheckColumn(int x) + { + for (int i = 0; i < CellsY; i++) + if (!Data[x, i]) + return false; + return true; + } + + public bool CheckRow(int y) + { + for (int i = 0; i < CellsX; i++) + if (!Data[i, y]) + return false; + return true; + } + + public bool this[int x, int y] + { + get + { + if (x >= 0 && y >= 0 && x < CellsX && y < CellsY) + return Data[x, y]; + else + return false; + } + + set + { + Data[x, y] = value; + } + } + + public int CellsX + { + get + { + return Data.Columns; + } + } + + public int CellsY + { + get + { + return Data.Rows; + } + } + + public override float Width + { + get + { + return CellWidth * CellsX; + } + + set + { + throw new NotImplementedException(); + } + } + + public override float Height + { + get + { + return CellHeight * CellsY; + } + + set + { + throw new NotImplementedException(); + } + } + + public bool IsEmpty + { + get + { + for (int i = 0; i < CellsX; i++) + for (int j = 0; j < CellsY; j++) + if (Data[i, j]) + return false; + return true; + } + } + + public override float Left + { + get { return Position.X; } + set { Position.X = value; } + } + + public override float Top + { + get { return Position.Y; } + set { Position.Y = value; } + } + + public override float Right + { + get { return Position.X + Width; } + set { Position.X = value - Width; } + } + + public override float Bottom + { + get { return Position.Y + Height; } + set { Position.Y = value - Height; } + } + + public override Collider Clone() + { + return new Grid(CellWidth, CellHeight, Data.Clone()); + } + + public override void Render(Camera camera, Color color) + { + if (camera == null) + { + for (int i = 0; i < CellsX; i++) + for (int j = 0; j < CellsY; j++) + if (Data[i, j]) + Draw.HollowRect(AbsoluteLeft + i * CellWidth, AbsoluteTop + j * CellHeight, CellWidth, CellHeight, color); + } + else + { + int left = (int)Math.Max(0, ((camera.Left - AbsoluteLeft) / CellWidth)); + int right = (int)Math.Min(CellsX - 1, Math.Ceiling((camera.Right - AbsoluteLeft) / CellWidth)); + int top = (int)Math.Max(0, ((camera.Top - AbsoluteTop) / CellHeight)); + int bottom = (int)Math.Min(CellsY - 1, Math.Ceiling((camera.Bottom - AbsoluteTop) / CellHeight)); + + for (int tx = left; tx <= right; tx++) + for (int ty = top; ty <= bottom; ty++) + if (Data[tx, ty]) + Draw.HollowRect(AbsoluteLeft + tx * CellWidth, AbsoluteTop + ty * CellHeight, CellWidth, CellHeight, color); + } + } + + /* + * Checking against other colliders + */ + + override public bool Collide(Vector2 point) + { + if (point.X >= AbsoluteLeft && point.Y >= AbsoluteTop && point.X < AbsoluteRight && point.Y < AbsoluteBottom) + return Data[(int)((point.X - AbsoluteLeft) / CellWidth), (int)((point.Y - AbsoluteTop) / CellHeight)]; + else + return false; + } + + public override bool Collide(Rectangle rect) + { + if (rect.Intersects(Bounds)) + { + int x = (int)((rect.Left - AbsoluteLeft) / CellWidth); + int y = (int)((rect.Top - AbsoluteTop) / CellHeight); + int w = (int)((rect.Right - AbsoluteLeft - 1) / CellWidth) - x + 1; + int h = (int)((rect.Bottom - AbsoluteTop - 1) / CellHeight) - y + 1; + + return CheckRect(x, y, w, h); + } + else + return false; + } + + public override bool Collide(Vector2 from, Vector2 to) + { + from -= AbsolutePosition; + to -= AbsolutePosition; + from /= new Vector2(CellWidth, CellHeight); + to /= new Vector2(CellWidth, CellHeight); + + bool steep = Math.Abs(to.Y - from.Y) > Math.Abs(to.X - from.X); + if (steep) + { + float temp = from.X; + from.X = from.Y; + from.Y = temp; + + temp = to.X; + to.X = to.Y; + to.Y = temp; + } + if (from.X > to.X) + { + Vector2 temp = from; + from = to; + to = temp; + } + + float error = 0; + float deltaError = Math.Abs(to.Y - from.Y) / (to.X - from.X); + + int yStep = (from.Y < to.Y) ? 1 : -1; + int y = (int)from.Y; + int toX = (int)to.X; + + for (int x = (int)from.X; x <= toX; x++) + { + if (steep) + { + if (this[y, x]) + return true; + } + else + { + if (this[x, y]) + return true; + } + + error += deltaError; + if (error >= .5f) + { + y += yStep; + error -= 1.0f; + } + } + + return false; + } + + public override bool Collide(Hitbox hitbox) + { + return Collide(hitbox.Bounds); + } + + public override bool Collide(Grid grid) + { + throw new NotImplementedException(); + } + + public override bool Collide(Circle circle) + { + return false; + } + + public override bool Collide(ColliderList list) + { + return list.Collide(this); + } + + /* + * Static utilities + */ + + public static bool IsBitstringEmpty(string bitstring) + { + for (int i = 0; i < bitstring.Length; i++) + { + if (bitstring[i] == '1') + return false; + } + + return true; + } + } +} diff --git a/MonocleEngineDemo/Monocle/Colliders/Hitbox.cs b/MonocleEngineDemo/Monocle/Colliders/Hitbox.cs new file mode 100644 index 0000000..554602a --- /dev/null +++ b/MonocleEngineDemo/Monocle/Colliders/Hitbox.cs @@ -0,0 +1,161 @@ +using Microsoft.Xna.Framework; +using System; + +namespace Monocle +{ + public class Hitbox : Collider + { + private float width; + private float height; + + public Hitbox(float width, float height, float x = 0, float y = 0) + { + this.width = width; + this.height = height; + + Position.X = x; + Position.Y = y; + } + + public override float Width + { + get { return width; } + set { width = value; } + } + + public override float Height + { + get { return height; } + set { height = value; } + } + + public override float Left + { + get { return Position.X; } + set { Position.X = value; } + } + + public override float Top + { + get { return Position.Y; } + set { Position.Y = value; } + } + + public override float Right + { + get { return Position.X + Width; } + set { Position.X = value - Width; } + } + + public override float Bottom + { + get { return Position.Y + Height; } + set { Position.Y = value - Height; } + } + + public bool Intersects(Hitbox hitbox) + { + return AbsoluteLeft < hitbox.AbsoluteRight && AbsoluteRight > hitbox.AbsoluteLeft && AbsoluteBottom > hitbox.AbsoluteTop && AbsoluteTop < hitbox.AbsoluteBottom; + } + + public bool Intersects(float x, float y, float width, float height) + { + return AbsoluteRight > x && AbsoluteBottom > y && AbsoluteLeft < x + width && AbsoluteTop < y + height; + } + + public override Collider Clone() + { + return new Hitbox(width, height, Position.X, Position.Y); + } + + public override void Render(Camera camera, Color color) + { + Draw.HollowRect(AbsoluteX, AbsoluteY, Width, Height, color); + } + + public void SetFromRectangle(Rectangle rect) + { + Position = new Vector2(rect.X, rect.Y); + Width = rect.Width; + Height = rect.Height; + } + + public void Set(float x, float y, float w, float h) + { + Position = new Vector2(x, y); + Width = w; + Height = h; + } + + /* + * Get edges + */ + + public void GetTopEdge(out Vector2 from, out Vector2 to) + { + from.X = AbsoluteLeft; + to.X = AbsoluteRight; + from.Y = to.Y = AbsoluteTop; + } + + public void GetBottomEdge(out Vector2 from, out Vector2 to) + { + from.X = AbsoluteLeft; + to.X = AbsoluteRight; + from.Y = to.Y = AbsoluteBottom; + } + + public void GetLeftEdge(out Vector2 from, out Vector2 to) + { + from.Y = AbsoluteTop; + to.Y = AbsoluteBottom; + from.X = to.X = AbsoluteLeft; + } + + public void GetRightEdge(out Vector2 from, out Vector2 to) + { + from.Y = AbsoluteTop; + to.Y = AbsoluteBottom; + from.X = to.X = AbsoluteRight; + } + + /* + * Checking against other colliders + */ + + public override bool Collide(Vector2 point) + { + return Monocle.Collide.RectToPoint(AbsoluteLeft, AbsoluteTop, Width, Height, point); + } + + public override bool Collide(Rectangle rect) + { + return AbsoluteRight > rect.Left && AbsoluteBottom > rect.Top && AbsoluteLeft < rect.Right && AbsoluteTop < rect.Bottom; + } + + public override bool Collide(Vector2 from, Vector2 to) + { + return Monocle.Collide.RectToLine(AbsoluteLeft, AbsoluteTop, Width, Height, from, to); + } + + public override bool Collide(Hitbox hitbox) + { + return Intersects(hitbox); + } + + public override bool Collide(Grid grid) + { + return grid.Collide(Bounds); + } + + public override bool Collide(Circle circle) + { + return Monocle.Collide.RectToCircle(AbsoluteLeft, AbsoluteTop, Width, Height, circle.AbsolutePosition, circle.Radius); + } + + public override bool Collide(ColliderList list) + { + return list.Collide(this); + } + } +} diff --git a/MonocleEngineDemo/Monocle/Components/CollidableComponent.cs b/MonocleEngineDemo/Monocle/Components/CollidableComponent.cs new file mode 100644 index 0000000..9ec4b0a --- /dev/null +++ b/MonocleEngineDemo/Monocle/Components/CollidableComponent.cs @@ -0,0 +1,324 @@ +using Microsoft.Xna.Framework; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Monocle +{ + public class CollidableComponent : Component + { + public bool Collidable; + + private Collider collider; + + public CollidableComponent(bool active, bool visible, bool colllidable) + : base(active, visible) + { + Collidable = colllidable; + } + + public override void Added(Entity entity) + { + base.Added(entity); + if (collider != null) + collider.Entity = entity; + } + + public override void Removed(Entity entity) + { + if (collider != null) + collider.Entity = null; + base.Removed(entity); + } + + public Collider Collider + { + get + { + if (collider == null) + return Entity.Collider; + else + return collider; + } + + set + { + if (value == collider) + return; +#if DEBUG + if (value.Entity != null) + throw new Exception("Setting an Entity's Collider to a Collider already in use by another object"); +#endif + if (collider != null) + collider.Removed(); + collider = value; + if (collider != null) + collider.Added(this); + } + } + + public float Width + { + get + { + if (collider == null) + return Entity.Width; + else + return collider.Width; + } + } + + public float Height + { + get + { + if (collider == null) + return Entity.Height; + else + return collider.Height; + } + } + + public float Left + { + get + { + if (collider == null) + return Entity.Left; + else + return Entity.X + collider.Left; + } + + set + { + if (collider == null) + Entity.Left = value; + else + Entity.X = value - collider.Left; + } + } + + public float Right + { + get + { + if (collider == null) + return Entity.Right; + else + return Entity.X + collider.Right; + } + + set + { + if (collider == null) + Entity.Right = value; + else + Entity.X = value - collider.Right; + } + } + + public float Top + { + get + { + if (collider == null) + return Entity.Top; + else + return Entity.Y + collider.Top; + } + + set + { + if (collider == null) + Entity.Top = value; + else + Entity.Y = value - collider.Top; + } + } + + public float Bottom + { + get + { + if (collider == null) + return Entity.Bottom; + else + return Entity.Y + collider.Bottom; + } + + set + { + if (collider == null) + Entity.Bottom = value; + else + Entity.Y = value - collider.Bottom; + } + } + + public float CenterX + { + get + { + if (collider == null) + return Entity.CenterX; + else + return Entity.X + collider.CenterX; + } + + set + { + if (collider == null) + Entity.CenterX = value; + else + Entity.X = value - collider.CenterX; + } + } + + public float CenterY + { + get + { + if (collider == null) + return Entity.CenterY; + else + return Entity.Y + collider.CenterY; + } + + set + { + if (collider == null) + Entity.CenterY = value; + else + Entity.Y = value - collider.CenterY; + } + } + + public Vector2 TopLeft + { + get + { + return new Vector2(Left, Top); + } + + set + { + Left = value.X; + Top = value.Y; + } + } + + public Vector2 TopRight + { + get + { + return new Vector2(Right, Top); + } + + set + { + Right = value.X; + Top = value.Y; + } + } + + public Vector2 BottomLeft + { + get + { + return new Vector2(Left, Bottom); + } + + set + { + Left = value.X; + Bottom = value.Y; + } + } + + public Vector2 BottomRight + { + get + { + return new Vector2(Right, Bottom); + } + + set + { + Right = value.X; + Bottom = value.Y; + } + } + + public Vector2 Center + { + get + { + return new Vector2(CenterX, CenterY); + } + + set + { + CenterX = value.X; + CenterY = value.Y; + } + } + + public Vector2 CenterLeft + { + get + { + return new Vector2(Left, CenterY); + } + + set + { + Left = value.X; + CenterY = value.Y; + } + } + + public Vector2 CenterRight + { + get + { + return new Vector2(Right, CenterY); + } + + set + { + Right = value.X; + CenterY = value.Y; + } + } + + public Vector2 TopCenter + { + get + { + return new Vector2(CenterX, Top); + } + + set + { + CenterX = value.X; + Top = value.Y; + } + } + + public Vector2 BottomCenter + { + get + { + return new Vector2(CenterX, Bottom); + } + + set + { + CenterX = value.X; + Bottom = value.Y; + } + } + } +} diff --git a/MonocleEngineDemo/Monocle/Components/Component.cs b/MonocleEngineDemo/Monocle/Components/Component.cs new file mode 100644 index 0000000..2af564d --- /dev/null +++ b/MonocleEngineDemo/Monocle/Components/Component.cs @@ -0,0 +1,98 @@ +using Microsoft.Xna.Framework; +using System; + +namespace Monocle +{ + public class Component + { + public Entity Entity { get; private set; } + public bool Active; + public bool Visible; + + public Component(bool active, bool visible) + { + Active = active; + Visible = visible; + } + + public virtual void Added(Entity entity) + { + Entity = entity; + if (Scene != null) + Scene.Tracker.ComponentAdded(this); + } + + public virtual void Removed(Entity entity) + { + if (Scene != null) + Scene.Tracker.ComponentRemoved(this); + Entity = null; + } + + public virtual void EntityAdded(Scene scene) + { + scene.Tracker.ComponentAdded(this); + } + + public virtual void EntityRemoved(Scene scene) + { + scene.Tracker.ComponentRemoved(this); + } + + public virtual void SceneEnd(Scene scene) + { + + } + + public virtual void EntityAwake() + { + + } + + public virtual void Update() + { + + } + + public virtual void Render() + { + + } + + public virtual void DebugRender(Camera camera) + { + + } + + public virtual void HandleGraphicsReset() + { + + } + + public virtual void HandleGraphicsCreate() + { + + } + + public void RemoveSelf() + { + if (Entity != null) + Entity.Remove(this); + } + + public T SceneAs() where T : Scene + { + return Scene as T; + } + + public T EntityAs() where T : Entity + { + return Entity as T; + } + + public Scene Scene + { + get { return Entity?.Scene; } + } + } +} diff --git a/MonocleEngineDemo/Monocle/Components/Graphics/GraphicsComponent.cs b/MonocleEngineDemo/Monocle/Components/Graphics/GraphicsComponent.cs new file mode 100644 index 0000000..6e8ae8d --- /dev/null +++ b/MonocleEngineDemo/Monocle/Components/Graphics/GraphicsComponent.cs @@ -0,0 +1,99 @@ +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; + +namespace Monocle +{ + public abstract class GraphicsComponent : Component + { + public Vector2 Position; + public Vector2 Origin; + public Vector2 Scale = Vector2.One; + public float Rotation; + public Color Color = Color.White; + public SpriteEffects Effects = SpriteEffects.None; + + public GraphicsComponent(bool active) + : base(active, true) + { + + } + + public float X + { + get { return Position.X; } + set { Position.X = value; } + } + + public float Y + { + get { return Position.Y; } + set { Position.Y = value; } + } + + public bool FlipX + { + get + { + return (Effects & SpriteEffects.FlipHorizontally) == SpriteEffects.FlipHorizontally; + } + + set + { + Effects = value ? (Effects | SpriteEffects.FlipHorizontally) : (Effects & ~SpriteEffects.FlipHorizontally); + } + } + + public bool FlipY + { + get + { + return (Effects & SpriteEffects.FlipVertically) == SpriteEffects.FlipVertically; + } + + set + { + Effects = value ? (Effects | SpriteEffects.FlipVertically) : (Effects & ~SpriteEffects.FlipVertically); + } + } + + public Vector2 RenderPosition + { + get + { + return (Entity == null ? Vector2.Zero : Entity.Position) + Position; + } + + set + { + Position = value - (Entity == null ? Vector2.Zero : Entity.Position); + } + } + + public void DrawOutline(int offset = 1) + { + DrawOutline(Color.Black, offset); + } + + public void DrawOutline(Color color, int offset = 1) + { + Vector2 pos = Position; + Color was = Color; + Color = color; + + for (int i = -1; i < 2; i++) + { + for (int j = -1; j < 2; j++) + { + if (i != 0 || j != 0) + { + Position = pos + new Vector2(i * offset, j * offset); + Render(); + } + } + } + + Position = pos; + Color = was; + } + } +} diff --git a/MonocleEngineDemo/Monocle/Components/Graphics/Image.cs b/MonocleEngineDemo/Monocle/Components/Graphics/Image.cs new file mode 100644 index 0000000..ff33933 --- /dev/null +++ b/MonocleEngineDemo/Monocle/Components/Graphics/Image.cs @@ -0,0 +1,65 @@ +using Microsoft.Xna.Framework; + +namespace Monocle +{ + public class Image : GraphicsComponent + { + public MTexture Texture; + + public Image(MTexture texture) + : base(false) + { + Texture = texture; + } + + internal Image(MTexture texture, bool active) + : base(active) + { + Texture = texture; + } + + public override void Render() + { + if (Texture != null) + Texture.Draw(RenderPosition, Origin, Color, Scale, Rotation, Effects); + } + + public virtual float Width + { + get { return Texture.Width; } + } + + public virtual float Height + { + get { return Texture.Height; } + } + + public Image SetOrigin(float x, float y) + { + Origin.X = x; + Origin.Y = y; + return this; + } + + public Image CenterOrigin() + { + Origin.X = Width / 2f; + Origin.Y = Height / 2f; + return this; + } + + public Image JustifyOrigin(Vector2 at) + { + Origin.X = Width * at.X; + Origin.Y = Height * at.Y; + return this; + } + + public Image JustifyOrigin(float x, float y) + { + Origin.X = Width * x; + Origin.Y = Height * y; + return this; + } + } +} diff --git a/MonocleEngineDemo/Monocle/Components/Graphics/ParticleEmitter.cs b/MonocleEngineDemo/Monocle/Components/Graphics/ParticleEmitter.cs new file mode 100644 index 0000000..8642963 --- /dev/null +++ b/MonocleEngineDemo/Monocle/Components/Graphics/ParticleEmitter.cs @@ -0,0 +1,96 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.Xna.Framework; + +namespace Monocle +{ + public class ParticleEmitter : Component + { + + public ParticleSystem System; + public ParticleType Type; + + public Entity Track; + public float Interval; + public Vector2 Position; + public Vector2 Range; + public int Amount; + public float? Direction; + + private float timer = 0f; + + public ParticleEmitter(ParticleSystem system, ParticleType type, Vector2 position, Vector2 range, int amount, float interval) : base(true, false) + { + System = system; + Type = type; + Position = position; + Range = range; + Amount = amount; + Interval = interval; + } + + public ParticleEmitter(ParticleSystem system, ParticleType type, Vector2 position, Vector2 range, float direction, int amount, float interval) + : this(system, type, position, range, amount, interval) + { + Direction = direction; + } + + public ParticleEmitter(ParticleSystem system, ParticleType type, Entity track, Vector2 position, Vector2 range, float direction, int amount, float interval) + : this(system, type, position, range, amount, interval) + { + Direction = direction; + Track = track; + } + + public void SimulateCycle() + { + Simulate(Type.LifeMax); + } + + public void Simulate(float duration) + { + var steps = duration / Interval; + for (var i = 0; i < steps; i++) + { + for (int j = 0; j < Amount; j++) + { + // create the particle + var particle = new Particle(); + var pos = Entity.Position + Position + Calc.Random.Range(-Range, Range); + if (Direction.HasValue) + particle = Type.Create(ref particle, pos, Direction.Value); + else + particle = Type.Create(ref particle, pos); + particle.Track = Track; + + // simulate for a duration + var simulateFor = duration - Interval * i; + if (particle.SimulateFor(simulateFor)) + System.Add(particle); + } + } + } + + public void Emit() + { + if (Direction.HasValue) + System.Emit(Type, Amount, Entity.Position + Position, Range, Direction.Value); + else + System.Emit(Type, Amount, Entity.Position + Position, Range); + } + + public override void Update() + { + timer -= Engine.DeltaTime; + if (timer <= 0) + { + timer = Interval; + Emit(); + } + } + + } +} diff --git a/MonocleEngineDemo/Monocle/Components/Graphics/PixelText.cs b/MonocleEngineDemo/Monocle/Components/Graphics/PixelText.cs new file mode 100644 index 0000000..fad7ea3 --- /dev/null +++ b/MonocleEngineDemo/Monocle/Components/Graphics/PixelText.cs @@ -0,0 +1,125 @@ +using Microsoft.Xna.Framework; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Monocle +{ + public class PixelText : Component + { + + private struct Char + { + public Vector2 Offset; + public PixelFontCharacter CharData; + public Rectangle Bounds; + } + + private List characters = new List(); + private PixelFont font; + private PixelFontSize size; + private string text; + private bool dirty; + + public PixelFont Font + { + get { return font; } + set + { + if (value != font) + dirty = true; + font = value; + } + } + + public float Size + { + get { return size.Size; } + set + { + if (value != size.Size) + dirty = true; + size = font.Get(value); + } + } + + public string Text + { + get { return text; } + set + { + if (value != text) + dirty = true; + text = value; + } + } + + public Vector2 Position = new Vector2(); + public Color Color = Color.White; + public Vector2 Scale = Vector2.One; + public int Width { get; private set; } + public int Height { get; private set; } + + public PixelText(PixelFont font, string text, Color color) + : base(false, true) + { + Font = font; + Text = text; + Color = color; + Text = text; + size = Font.Sizes[0]; + Refresh(); + } + + public void Refresh() + { + dirty = false; + characters.Clear(); + + var widest = 0; + var lines = 1; + var offset = Vector2.Zero; + + for (int i = 0; i < text.Length; i++) + { + // new line + if (text[i] == '\n') + { + offset.X = 0; + offset.Y += size.LineHeight; + lines++; + } + + // add char + var fontChar = size.Get(text[i]); + if (fontChar != null) + { + characters.Add(new Char() + { + Offset = offset + new Vector2(fontChar.XOffset, fontChar.YOffset), + CharData = fontChar, + Bounds = fontChar.Texture.ClipRect, + }); + + if (offset.X > widest) + widest = (int)offset.X; + offset.X += fontChar.XAdvance; + } + } + + Width = widest; + Height = lines * size.LineHeight; + } + + public override void Render() + { + if (dirty) + Refresh(); + + for (var i = 0; i < characters.Count; i++) + characters[i].CharData.Texture.Draw(Position + characters[i].Offset, Vector2.Zero, Color); + } + + } +} diff --git a/MonocleEngineDemo/Monocle/Components/Graphics/Sprite.cs b/MonocleEngineDemo/Monocle/Components/Graphics/Sprite.cs new file mode 100644 index 0000000..ba577e0 --- /dev/null +++ b/MonocleEngineDemo/Monocle/Components/Graphics/Sprite.cs @@ -0,0 +1,520 @@ +using Microsoft.Xna.Framework; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Monocle +{ + public class Sprite : Image + { + public float Rate = 1f; + public bool UseRawDeltaTime; + public Vector2? Justify; + public Action OnFinish; + public Action OnLoop; + public Action OnFrameChange; + public Action OnLastFrame; + public Action OnChange; + + private Atlas atlas; + public string Path; + private Dictionary animations; + private Animation currentAnimation; + private float animationTimer; + private int width; + private int height; + + public Sprite(Atlas atlas, string path) + : base(null, true) + { + this.atlas = atlas; + this.Path = path; + animations = new Dictionary(StringComparer.OrdinalIgnoreCase); + CurrentAnimationID = ""; + } + + public void Reset(Atlas atlas, string path) + { + this.atlas = atlas; + this.Path = path; + animations = new Dictionary(StringComparer.OrdinalIgnoreCase); + + currentAnimation = null; + CurrentAnimationID = ""; + OnFinish = null; + OnLoop = null; + OnFrameChange = null; + OnChange = null; + Animating = false; + } + + public MTexture GetFrame(string animation, int frame) + { + return animations[animation].Frames[frame]; + } + + public Vector2 Center + { + get + { + return new Vector2(Width / 2, Height / 2); + } + } + + public override void Update() + { + if (Animating) + { + //Timer + if (UseRawDeltaTime) + animationTimer += Engine.RawDeltaTime * Rate; + else + animationTimer += Engine.DeltaTime * Rate; + + //Next Frame + if (Math.Abs(animationTimer) >= currentAnimation.Delay) + { + CurrentAnimationFrame += Math.Sign(animationTimer); + animationTimer -= Math.Sign(animationTimer) * currentAnimation.Delay; + + //End of Animation + if (CurrentAnimationFrame < 0 || CurrentAnimationFrame >= currentAnimation.Frames.Length) + { + var was = CurrentAnimationID; + if (OnLastFrame != null) + OnLastFrame(CurrentAnimationID); + + // only do stuff if OnLastFrame didn't just change the animation + if (was == CurrentAnimationID) + { + //Looped + if (currentAnimation.Goto != null) + { + CurrentAnimationID = currentAnimation.Goto.Choose(); + + if (OnChange != null) + OnChange(LastAnimationID, CurrentAnimationID); + + LastAnimationID = CurrentAnimationID; + currentAnimation = animations[LastAnimationID]; + if (CurrentAnimationFrame < 0) + CurrentAnimationFrame = currentAnimation.Frames.Length - 1; + else + CurrentAnimationFrame = 0; + + SetFrame(currentAnimation.Frames[CurrentAnimationFrame]); + if (OnLoop != null) + OnLoop(CurrentAnimationID); + } + else + { + //Ended + if (CurrentAnimationFrame < 0) + CurrentAnimationFrame = 0; + else + CurrentAnimationFrame = currentAnimation.Frames.Length - 1; + + Animating = false; + var id = CurrentAnimationID; + CurrentAnimationID = ""; + currentAnimation = null; + animationTimer = 0; + if (OnFinish != null) + OnFinish(id); + } + } + } + else + { + //Continue Animation + SetFrame(currentAnimation.Frames[CurrentAnimationFrame]); + } + } + } + } + + private void SetFrame(MTexture texture) + { + if (texture == Texture) + return; + + Texture = texture; + if (Justify.HasValue) + Origin = new Vector2(Texture.Width * Justify.Value.X, Texture.Height * Justify.Value.Y); + if (OnFrameChange != null) + OnFrameChange(CurrentAnimationID); + } + + public void SetAnimationFrame(int frame) + { + animationTimer = 0; + CurrentAnimationFrame = frame % currentAnimation.Frames.Length; + SetFrame(currentAnimation.Frames[CurrentAnimationFrame]); + } + + #region Define Animations + + public void AddLoop(string id, string path, float delay) + { + animations[id] = new Animation() + { + Delay = delay, + Frames = GetFrames(path), + Goto = new Chooser(id, 1f) + }; + } + + public void AddLoop(string id, string path, float delay, params int[] frames) + { + animations[id] = new Animation() + { + Delay = delay, + Frames = GetFrames(path, frames), + Goto = new Chooser(id, 1f) + }; + } + + public void Add(string id, string path) + { + animations[id] = new Animation() + { + Delay = 0, + Frames = GetFrames(path), + Goto = null + }; + } + + public void Add(string id, string path, float delay) + { + animations[id] = new Animation() + { + Delay = delay, + Frames = GetFrames(path), + Goto = null + }; + } + + public void Add(string id, string path, float delay, params int[] frames) + { + animations[id] = new Animation() + { + Delay = delay, + Frames = GetFrames(path, frames), + Goto = null + }; + } + + public void Add(string id, string path, float delay, string into) + { + animations[id] = new Animation() + { + Delay = delay, + Frames = GetFrames(path), + Goto = Chooser.FromString(into) + }; + } + + public void Add(string id, string path, float delay, Chooser into) + { + animations[id] = new Animation() + { + Delay = delay, + Frames = GetFrames(path), + Goto = into + }; + } + + public void Add(string id, string path, float delay, string into, params int[] frames) + { + animations[id] = new Animation() + { + Delay = delay, + Frames = GetFrames(path, frames), + Goto = Chooser.FromString(into) + }; + } + + public void Add(string id, string path, float delay, Chooser into, params int[] frames) + { + animations[id] = new Animation() + { + Delay = delay, + Frames = GetFrames(path, frames), + Goto = into + }; + } + + private MTexture[] GetFrames(string path, int[] frames = null) + { + MTexture[] ret; + + if (frames == null || frames.Length <= 0) + ret = atlas.GetAtlasSubtextures(this.Path + path).ToArray(); + else + { + var fullPath = this.Path + path; + var finalFrames = new MTexture[frames.Length]; + for (int i = 0; i < frames.Length; i++) + { + var frame = atlas.GetAtlasSubtexturesAt(fullPath, frames[i]); + if (frame == null) + + throw new Exception("Can't find sprite " + fullPath + " with index " + frames[i]); + finalFrames[i] = frame; + } + ret = finalFrames; + } + +#if DEBUG + if (ret.Length == 0) + throw new Exception("No frames found for animation path '" + this.Path + path + "'!"); +#endif + + width = Math.Max(ret[0].Width, width); + height = Math.Max(ret[0].Height, height); + + return ret; + } + + public void ClearAnimations() + { + animations.Clear(); + } + + #endregion + + #region Animation Playback + + public void Play(string id, bool restart = false, bool randomizeFrame = false) + { + if (CurrentAnimationID != id || restart) + { +#if DEBUG + if (!animations.ContainsKey(id)) + throw new Exception("No Animation defined for ID: " + id); +#endif + if (OnChange != null) + OnChange(LastAnimationID, id); + + LastAnimationID = CurrentAnimationID = id; + currentAnimation = animations[id]; + Animating = currentAnimation.Delay > 0; + + if (randomizeFrame) + { + animationTimer = Calc.Random.NextFloat(currentAnimation.Delay); + CurrentAnimationFrame = Calc.Random.Next(currentAnimation.Frames.Length); + } + else + { + animationTimer = 0; + CurrentAnimationFrame = 0; + } + + SetFrame(currentAnimation.Frames[CurrentAnimationFrame]); + } + } + + public void PlayOffset(string id, float offset, bool restart = false) + { + if (CurrentAnimationID != id || restart) + { +#if DEBUG + if (!animations.ContainsKey(id)) + throw new Exception("No Animation defined for ID: " + id); +#endif + if (OnChange != null) + OnChange(LastAnimationID, id); + + LastAnimationID = CurrentAnimationID = id; + currentAnimation = animations[id]; + + if (currentAnimation.Delay > 0) + { + Animating = true; + float at = (currentAnimation.Delay * currentAnimation.Frames.Length) * offset; + + CurrentAnimationFrame = 0; + while (at >= currentAnimation.Delay) + { + CurrentAnimationFrame++; + at -= currentAnimation.Delay; + } + + CurrentAnimationFrame %= currentAnimation.Frames.Length; + animationTimer = at; + SetFrame(currentAnimation.Frames[CurrentAnimationFrame]); + } + else + { + animationTimer = 0; + Animating = false; + CurrentAnimationFrame = 0; + SetFrame(currentAnimation.Frames[0]); + } + } + } + + public IEnumerator PlayRoutine(string id, bool restart = false) + { + Play(id, restart); + return PlayUtil(); + } + + public IEnumerator ReverseRoutine(string id, bool restart = false) + { + Reverse(id, restart); + return PlayUtil(); + } + + private IEnumerator PlayUtil() + { + while (Animating) + yield return null; + } + + public void Reverse(string id, bool restart = false) + { + Play(id, restart); + if (Rate > 0) + Rate *= -1; + } + + public bool Has(string id) + { + return id != null && animations.ContainsKey(id); + } + + public void Stop() + { + Animating = false; + currentAnimation = null; + CurrentAnimationID = ""; + } + + #endregion + + #region Properties + + public bool Animating + { + get; private set; + } + + public string CurrentAnimationID + { + get; private set; + } + + public string LastAnimationID + { + get; private set; + } + + public int CurrentAnimationFrame + { + get; private set; + } + + public int CurrentAnimationTotalFrames + { + get + { + if (currentAnimation != null) + return currentAnimation.Frames.Length; + else + return 0; + } + } + + public override float Width + { + get + { + return width; + } + } + + public override float Height + { + get + { + return height; + } + } + + #endregion + + #region Cloning from SpriteBank + + internal Sprite() + : base(null, true) + { + + } + + internal Sprite CreateClone() + { + return CloneInto(new Sprite()); + } + + internal Sprite CloneInto(Sprite clone) + { + clone.Texture = Texture; + clone.Position = Position; + clone.Justify = Justify; + clone.Origin = Origin; + + clone.animations = new Dictionary(animations, StringComparer.OrdinalIgnoreCase); + clone.currentAnimation = currentAnimation; + clone.animationTimer = animationTimer; + clone.width = width; + clone.height = height; + + clone.Animating = Animating; + clone.CurrentAnimationID = CurrentAnimationID; + clone.LastAnimationID = LastAnimationID; + clone.CurrentAnimationFrame = CurrentAnimationFrame; + + return clone; + } + + #endregion + + public void DrawSubrect(Vector2 offset, Rectangle rectangle) + { + if (Texture != null) + { + var clip = Texture.GetRelativeRect(rectangle); + var clipOffset = new Vector2(-Math.Min(rectangle.X - Texture.DrawOffset.X, 0), -Math.Min(rectangle.Y - Texture.DrawOffset.Y, 0)); + Draw.SpriteBatch.Draw(Texture.Texture, RenderPosition + offset, clip, Color, Rotation, Origin - clipOffset, Scale, Effects, 0); + } + } + + public void LogAnimations() + { + StringBuilder str = new StringBuilder(); + + foreach (var kv in animations) + { + var anim = kv.Value; + + str.Append(kv.Key); + str.Append("\n{\n\t"); + str.Append(string.Join("\n\t", (object[])anim.Frames)); + str.Append("\n}\n"); + } + + Calc.Log(str.ToString()); + } + + private class Animation + { + public float Delay; + public MTexture[] Frames; + public Chooser Goto; + } + } +} diff --git a/MonocleEngineDemo/Monocle/Components/Graphics/Spritesheet.cs b/MonocleEngineDemo/Monocle/Components/Graphics/Spritesheet.cs new file mode 100644 index 0000000..2cabd16 --- /dev/null +++ b/MonocleEngineDemo/Monocle/Components/Graphics/Spritesheet.cs @@ -0,0 +1,243 @@ +using Microsoft.Xna.Framework; +using System; +using System.Collections.Generic; + +namespace Monocle +{ + public class Spritesheet : Image + { + public int CurrentFrame; + public float Rate = 1; + public bool UseRawDeltaTime; + public Action OnFinish; + public Action OnLoop; + public Action OnAnimate; + + private Dictionary animations; + private Animation currentAnimation; + private float animationTimer; + private bool played; + + public Spritesheet(MTexture texture, int frameWidth, int frameHeight, int frameSep = 0) + : base(texture, true) + { + SetFrames(texture, frameWidth, frameHeight, frameSep); + animations = new Dictionary(); + } + + public void SetFrames(MTexture texture, int frameWidth, int frameHeight, int frameSep = 0) + { + List frames = new List(); + int x = 0, y = 0; + + while (y <= texture.Height - frameHeight) + { + while (x <= texture.Width - frameWidth) + { + frames.Add(texture.GetSubtexture(x, y, frameWidth, frameHeight)); + x += frameWidth + frameSep; + } + + y += frameHeight + frameSep; + x = 0; + } + + Frames = frames.ToArray(); + } + + public override void Update() + { + if (Animating && currentAnimation.Delay > 0) + { + //Timer + if (UseRawDeltaTime) + animationTimer += Engine.RawDeltaTime * Rate; + else + animationTimer += Engine.DeltaTime * Rate; + + //Next Frame + if (Math.Abs(animationTimer) >= currentAnimation.Delay) + { + CurrentAnimationFrame += Math.Sign(animationTimer); + animationTimer -= Math.Sign(animationTimer) * currentAnimation.Delay; + + //End of Animation + if (CurrentAnimationFrame < 0 || CurrentAnimationFrame >= currentAnimation.Frames.Length) + { + //Looped + if (currentAnimation.Loop) + { + CurrentAnimationFrame -= Math.Sign(CurrentAnimationFrame) * currentAnimation.Frames.Length; + CurrentFrame = currentAnimation.Frames[CurrentAnimationFrame]; + + if (OnAnimate != null) + OnAnimate(CurrentAnimationID); + if (OnLoop != null) + OnLoop(CurrentAnimationID); + } + else + { + //Ended + if (CurrentAnimationFrame < 0) + CurrentAnimationFrame = 0; + else + CurrentAnimationFrame = currentAnimation.Frames.Length - 1; + + Animating = false; + animationTimer = 0; + if (OnFinish != null) + OnFinish(CurrentAnimationID); + } + } + else + { + //Continue Animation + CurrentFrame = currentAnimation.Frames[CurrentAnimationFrame]; + if (OnAnimate != null) + OnAnimate(CurrentAnimationID); + } + } + } + } + + public override void Render() + { + Texture = Frames[CurrentFrame]; + base.Render(); + } + + #region Animation Definition + + public void Add(T id, bool loop, float delay, params int[] frames) + { +#if DEBUG + foreach (var i in frames) + if (i >= Frames.Length) + throw new IndexOutOfRangeException("Specified frames is out of max range for this Spritesheet"); +#endif + + animations[id] = new Animation() + { + Delay = delay, + Frames = frames, + Loop = loop, + }; + } + + public void Add(T id, float delay, params int[] frames) + { + Add(id, true, delay, frames); + } + + public void Add(T id, int frame) + { + Add(id, false, 0, frame); + } + + public void ClearAnimations() + { + animations.Clear(); + } + + #endregion + + #region Animation Playback + + public bool IsPlaying(T id) + { + if (!played) + return false; + else if (CurrentAnimationID == null) + return id == null; + else + return CurrentAnimationID.Equals(id); + } + + public void Play(T id, bool restart = false) + { + if (!IsPlaying(id) || restart) + { +#if DEBUG + if (!animations.ContainsKey(id)) + throw new Exception("No Animation defined for ID: " + id.ToString()); +#endif + CurrentAnimationID = id; + currentAnimation = animations[id]; + animationTimer = 0; + CurrentAnimationFrame = 0; + played = true; + + Animating = currentAnimation.Frames.Length > 1; + CurrentFrame = currentAnimation.Frames[0]; + } + } + + public void Reverse(T id, bool restart = false) + { + Play(id, restart); + if (Rate > 0) + Rate *= -1; + } + + public void Stop() + { + Animating = false; + played = false; + } + + #endregion + + #region Properties + + public MTexture[] Frames + { + get; private set; + } + + public bool Animating + { + get; private set; + } + + public T CurrentAnimationID + { + get; private set; + } + + public int CurrentAnimationFrame + { + get; private set; + } + + public override float Width + { + get + { + if (Frames.Length > 0) + return Frames[0].Width; + else + return 0; + } + } + + public override float Height + { + get + { + if (Frames.Length > 0) + return Frames[0].Height; + else + return 0; + } + } + + #endregion + + private struct Animation + { + public float Delay; + public int[] Frames; + public bool Loop; + } + } +} diff --git a/MonocleEngineDemo/Monocle/Components/Graphics/Text/NumberText.cs b/MonocleEngineDemo/Monocle/Components/Graphics/Text/NumberText.cs new file mode 100644 index 0000000..a75472d --- /dev/null +++ b/MonocleEngineDemo/Monocle/Components/Graphics/Text/NumberText.cs @@ -0,0 +1,70 @@ +using Microsoft.Xna.Framework.Graphics; +using System; + +namespace Monocle +{ + public class NumberText : GraphicsComponent + { + private SpriteFont font; + private int value; + private string prefix; + private string drawString; + + private bool centered; + + public Action OnValueUpdate; + + public NumberText(SpriteFont font, string prefix, int value, bool centered = false) + : base(false) + { + this.font = font; + this.prefix = prefix; + this.value = value; + this.centered = centered; + UpdateString(); + } + + public int Value + { + get + { + return value; + } + + set + { + if (this.value != value) + { + int oldValue = this.value; + this.value = value; + UpdateString(); + if (OnValueUpdate != null) + OnValueUpdate(oldValue); + } + } + } + + public void UpdateString() + { + drawString = prefix + value.ToString(); + + if (centered) + Origin = Calc.Floor(font.MeasureString(drawString) / 2); + } + + public override void Render() + { + Draw.SpriteBatch.DrawString(font, drawString, RenderPosition, Color, Rotation, Origin, Scale, Effects, 0); + } + + public float Width + { + get { return font.MeasureString(drawString).X; } + } + + public float Height + { + get { return font.MeasureString(drawString).Y; } + } + } +} diff --git a/MonocleEngineDemo/Monocle/Components/Graphics/Text/OutlineText.cs b/MonocleEngineDemo/Monocle/Components/Graphics/Text/OutlineText.cs new file mode 100644 index 0000000..b2dfc64 --- /dev/null +++ b/MonocleEngineDemo/Monocle/Components/Graphics/Text/OutlineText.cs @@ -0,0 +1,38 @@ +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; + +namespace Monocle +{ + public class OutlineText : Text + { + public Color OutlineColor = Color.Black; + public int OutlineOffset = 1; + + public OutlineText(SpriteFont font, string text, Vector2 position, Color color, HorizontalAlign horizontalAlign = HorizontalAlign.Center, VerticalAlign verticalAlign = VerticalAlign.Center) + : base(font, text, position, color, horizontalAlign, verticalAlign) + { + + } + + public OutlineText(SpriteFont font, string text, Vector2 position, HorizontalAlign horizontalAlign = HorizontalAlign.Center, VerticalAlign verticalAlign = VerticalAlign.Center) + : this(font, text, position, Color.White, horizontalAlign, verticalAlign) + { + + } + + public OutlineText(SpriteFont font, string text) + : this(font, text, Vector2.Zero, Color.White, HorizontalAlign.Center, VerticalAlign.Center) + { + + } + + public override void Render() + { + for (int i = -1; i < 2; i++) + for (int j = -1; j < 2; j++) + if (i != 0 || j != 0) + Draw.SpriteBatch.DrawString(Font, DrawText, RenderPosition + new Vector2(i * OutlineOffset, j * OutlineOffset), OutlineColor, Rotation, Origin, Scale, Effects, 0); + base.Render(); + } + } +} diff --git a/MonocleEngineDemo/Monocle/Components/Graphics/Text/Text.cs b/MonocleEngineDemo/Monocle/Components/Graphics/Text/Text.cs new file mode 100644 index 0000000..1a7e090 --- /dev/null +++ b/MonocleEngineDemo/Monocle/Components/Graphics/Text/Text.cs @@ -0,0 +1,115 @@ +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; + +namespace Monocle +{ + public class Text : GraphicsComponent + { + public enum HorizontalAlign { Left, Center, Right }; + public enum VerticalAlign { Top, Center, Bottom }; + + private SpriteFont font; + private string text; + private HorizontalAlign horizontalOrigin; + private VerticalAlign verticalOrigin; + private Vector2 size; + + public Text(SpriteFont font, string text, Vector2 position, Color color, HorizontalAlign horizontalAlign = HorizontalAlign.Center, VerticalAlign verticalAlign = VerticalAlign.Center) + : base(false) + { + this.font = font; + this.text = text; + Position = position; + Color = color; + this.horizontalOrigin = horizontalAlign; + this.verticalOrigin = verticalAlign; + UpdateSize(); + } + + public Text(SpriteFont font, string text, Vector2 position, HorizontalAlign horizontalAlign = HorizontalAlign.Center, VerticalAlign verticalAlign = VerticalAlign.Center) + : this(font, text, position, Color.White, horizontalAlign, verticalAlign) + { + + } + + public SpriteFont Font + { + get { return font; } + set + { + font = value; + UpdateSize(); + } + } + + public string DrawText + { + get { return text; } + set + { + text = value; + UpdateSize(); + } + } + + public HorizontalAlign HorizontalOrigin + { + get { return horizontalOrigin; } + set + { + horizontalOrigin = value; + UpdateCentering(); + } + } + + public VerticalAlign VerticalOrigin + { + get { return verticalOrigin; } + set + { + verticalOrigin = value; + UpdateCentering(); + } + } + + public float Width + { + get { return size.X; } + } + + public float Height + { + get { return size.Y; } + } + + private void UpdateSize() + { + size = font.MeasureString(text); + UpdateCentering(); + } + + private void UpdateCentering() + { + if (horizontalOrigin == HorizontalAlign.Left) + Origin.X = 0; + else if (horizontalOrigin == HorizontalAlign.Center) + Origin.X = size.X / 2; + else + Origin.X = size.X; + + if (verticalOrigin == VerticalAlign.Top) + Origin.Y = 0; + else if (verticalOrigin == VerticalAlign.Center) + Origin.Y = size.Y / 2; + else + Origin.Y = size.Y; + + Origin = Origin.Floor(); + } + + public override void Render() + { + Draw.SpriteBatch.DrawString(font, text, RenderPosition, Color, Rotation, Origin, Scale, Effects, 0); + } + } +} diff --git a/MonocleEngineDemo/Monocle/Components/Graphics/Text/TimerText.cs b/MonocleEngineDemo/Monocle/Components/Graphics/Text/TimerText.cs new file mode 100644 index 0000000..c38f683 --- /dev/null +++ b/MonocleEngineDemo/Monocle/Components/Graphics/Text/TimerText.cs @@ -0,0 +1,136 @@ +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using System; + +namespace Monocle +{ + public class TimerText : GraphicsComponent + { + private const float DELTA_TIME = 1 / 60f; + + public enum CountModes { Down, Up }; + public enum TimerModes { SecondsMilliseconds }; + + private SpriteFont font; + private int frames; + private TimerModes timerMode; + private Vector2 justify; + + public string Text { get; private set; } + public Action OnComplete; + public CountModes CountMode; + + public TimerText(SpriteFont font, TimerModes mode, CountModes countMode, int frames, Vector2 justify, Action onComplete = null) + : base(true) + { + this.font = font; + this.timerMode = mode; + this.CountMode = countMode; + this.frames = frames; + this.justify = justify; + +#if DEBUG + if (frames < 0) + throw new Exception("Frames must be larger than or equal to zero!"); +#endif + + OnComplete = onComplete; + + UpdateText(); + CalculateOrigin(); + } + + private void UpdateText() + { + switch (timerMode) + { + case TimerModes.SecondsMilliseconds: + float seconds = (frames / 60) + (frames % 60) * DELTA_TIME; + Text = seconds.ToString("0.00"); + break; + } + } + + private void CalculateOrigin() + { + Origin = (font.MeasureString(Text) * justify).Floor(); + } + + public override void Update() + { + base.Update(); + + if (CountMode == CountModes.Down) + { + if (frames > 0) + { + frames--; + if (frames == 0 && OnComplete != null) + OnComplete(); + + UpdateText(); + CalculateOrigin(); + } + } + else + { + frames++; + UpdateText(); + CalculateOrigin(); + } + } + + public override void Render() + { + Draw.SpriteBatch.DrawString(font, Text, RenderPosition, Color, Rotation, Origin, Scale, Effects, 0); + } + + public SpriteFont Font + { + get { return font; } + set + { + font = value; + CalculateOrigin(); + } + } + + public int Frames + { + get { return frames; } + set + { +#if DEBUG + if (value < 0) + throw new Exception("Frames must be larger than or equal to zero!"); +#endif + if (frames != value) + { + frames = value; + UpdateText(); + CalculateOrigin(); + } + } + } + + public Vector2 Justify + { + get { return justify; } + set + { + justify = value; + CalculateOrigin(); + } + } + + public float Width + { + get { return font.MeasureString(Text).X; } + } + + public float Height + { + get { return font.MeasureString(Text).Y; } + } + } +} diff --git a/MonocleEngineDemo/Monocle/Components/Graphics/TileGrid.cs b/MonocleEngineDemo/Monocle/Components/Graphics/TileGrid.cs new file mode 100644 index 0000000..e5fc73d --- /dev/null +++ b/MonocleEngineDemo/Monocle/Components/Graphics/TileGrid.cs @@ -0,0 +1,196 @@ +using Microsoft.Xna.Framework; +using Monocle; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Monocle +{ + public class TileGrid : Component + { + public Vector2 Position; + public Color Color = Color.White; + public int VisualExtend = 0; + public VirtualMap Tiles; + public Camera ClipCamera; + public float Alpha = 1f; + + public TileGrid(int tileWidth, int tileHeight, int tilesX, int tilesY) + : base(false, true) + { + TileWidth = tileWidth; + TileHeight = tileHeight; + Tiles = new VirtualMap(tilesX, tilesY); + } + + #region Properties + + public int TileWidth + { + get; private set; + } + + public int TileHeight + { + get; private set; + } + + public int TilesX + { + get + { + return Tiles.Columns; + } + } + + public int TilesY + { + get + { + return Tiles.Rows; + } + } + + #endregion + + public void Populate(Tileset tileset, int[,] tiles, int offsetX = 0, int offsetY = 0) + { + for (int x = 0; x < tiles.GetLength(0) && x + offsetX < TilesX; x++) + for (int y = 0; y < tiles.GetLength(1) && y + offsetY < TilesY; y++) + Tiles[x + offsetX, y + offsetY] = tileset[tiles[x, y]]; + } + + public void Overlay(Tileset tileset, int[,] tiles, int offsetX = 0, int offsetY = 0) + { + for (int x = 0; x < tiles.GetLength(0) && x + offsetX < TilesX; x++) + for (int y = 0; y < tiles.GetLength(1) && y + offsetY < TilesY; y++) + if (tiles[x, y] >= 0) + Tiles[x + offsetX, y + offsetY] = tileset[tiles[x, y]]; + } + + public void Extend(int left, int right, int up, int down) + { + Position -= new Vector2(left * TileWidth, up * TileHeight); + + int newWidth = TilesX + left + right; + int newHeight = TilesY + up + down; + if (newWidth <= 0 || newHeight <= 0) + { + Tiles = new VirtualMap(0, 0); + return; + } + + var newTiles = new VirtualMap(newWidth, newHeight); + + //Center + for (int x = 0; x < TilesX; x++) + { + for (int y = 0; y < TilesY; y++) + { + int atX = x + left; + int atY = y + up; + + if (atX >= 0 && atX < newWidth && atY >= 0 && atY < newHeight) + newTiles[atX, atY] = Tiles[x, y]; + } + } + + //Left + for (int x = 0; x < left; x++) + for (int y = 0; y < newHeight; y++) + newTiles[x, y] = Tiles[0, Calc.Clamp(y - up, 0, TilesY - 1)]; + + //Right + for (int x = newWidth - right; x < newWidth; x++) + for (int y = 0; y < newHeight; y++) + newTiles[x, y] = Tiles[TilesX - 1, Calc.Clamp(y - up, 0, TilesY - 1)]; + + //Top + for (int y = 0; y < up; y++) + for (int x = 0; x < newWidth; x++) + newTiles[x, y] = Tiles[Calc.Clamp(x - left, 0, TilesX - 1), 0]; + + //Bottom + for (int y = newHeight - down; y < newHeight; y++) + for (int x = 0; x < newWidth; x++) + newTiles[x, y] = Tiles[Calc.Clamp(x - left, 0, TilesX - 1), TilesY - 1]; + + Tiles = newTiles; + } + + public void FillRect(int x, int y, int columns, int rows, MTexture tile) + { + int left = Math.Max(0, x); + int top = Math.Max(0, y); + int right = Math.Min(TilesX, x + columns); + int bottom = Math.Min(TilesY, y + rows); + + for (int tx = left; tx < right; tx++) + for (int ty = top; ty < bottom; ty++) + Tiles[tx, ty] = tile; + } + + public void Clear() + { + for (int tx = 0; tx < TilesX; tx++) + for (int ty = 0; ty < TilesY; ty++) + Tiles[tx, ty] = null; + } + + public Rectangle GetClippedRenderTiles() + { + var pos = Entity.Position + Position; + + int left, top, right, bottom; + if (ClipCamera == null) + { + //throw new Exception("NULL CLIP: " + Entity.GetType().ToString()); + left = -VisualExtend; + top = -VisualExtend; + right = TilesX + VisualExtend; + bottom = TilesY + VisualExtend; + } + else + { + var camera = ClipCamera; + left = (int)Math.Max(0, Math.Floor((camera.Left - pos.X) / TileWidth) - VisualExtend); + top = (int)Math.Max(0, Math.Floor((camera.Top - pos.Y) / TileHeight) - VisualExtend); + right = (int)Math.Min(TilesX, Math.Ceiling((camera.Right - pos.X) / TileWidth) + VisualExtend); + bottom = (int)Math.Min(TilesY, Math.Ceiling((camera.Bottom - pos.Y) / TileHeight) + VisualExtend); + } + + // clamp + left = Math.Max(left, 0); + top = Math.Max(top, 0); + right = Math.Min(right, TilesX); + bottom = Math.Min(bottom, TilesY); + + return new Rectangle(left, top, right - left, bottom - top); + } + + public override void Render() + { + RenderAt(Entity.Position + Position); + } + + public void RenderAt(Vector2 position) + { + if (Alpha <= 0) + return; + + var clip = GetClippedRenderTiles(); + var color = Color * Alpha; + MTexture tile; + + for (int tx = clip.Left; tx < clip.Right; tx++) + for (int ty = clip.Top; ty < clip.Bottom; ty++) + { + tile = Tiles[tx, ty]; + if (tile != null) + tile.Draw(position + new Vector2(tx * TileWidth, ty * TileHeight), Vector2.Zero, color); + } + } + + } +} diff --git a/MonocleEngineDemo/Monocle/Components/Logic/Alarm.cs b/MonocleEngineDemo/Monocle/Components/Logic/Alarm.cs new file mode 100644 index 0000000..f293daf --- /dev/null +++ b/MonocleEngineDemo/Monocle/Components/Logic/Alarm.cs @@ -0,0 +1,110 @@ +using System; +using System.Collections.Generic; + +namespace Monocle +{ + public class Alarm : Component + { + public enum AlarmMode { Persist, Oneshot, Looping }; + + public Action OnComplete; + + public AlarmMode Mode { get; private set; } + public float Duration { get; private set; } + public float TimeLeft { get; private set; } + + #region Static + + private static Stack cached = new Stack(); + + public static Alarm Create(AlarmMode mode, Action onComplete, float duration = 1f, bool start = false) + { + Alarm alarm; + if (cached.Count == 0) + alarm = new Alarm(); + else + alarm = cached.Pop(); + + alarm.Init(mode, onComplete, duration, start); + return alarm; + } + + public static Alarm Set(Entity entity, float duration, Action onComplete, AlarmMode alarmMode = AlarmMode.Oneshot) + { + Alarm alarm = Alarm.Create(alarmMode, onComplete, duration, true); + entity.Add(alarm); + return alarm; + } + + #endregion + + private Alarm() + : base(false, false) + { + + } + + private void Init(AlarmMode mode, Action onComplete, float duration = 1f, bool start = false) + { +#if DEBUG + if (duration <= 0) + throw new Exception("Alarm duration cannot be less than zero"); +#endif + Mode = mode; + Duration = duration; + OnComplete = onComplete; + + Active = false; + TimeLeft = 0; + + if (start) + Start(); + } + + public override void Update() + { + TimeLeft -= Engine.DeltaTime; + if (TimeLeft <= 0) + { + TimeLeft = 0; + if (OnComplete != null) + OnComplete(); + + if (Mode == AlarmMode.Looping) + Start(); + else if (Mode == AlarmMode.Oneshot) + RemoveSelf(); + else if (TimeLeft <= 0) + Active = false; + } + } + + public override void Removed(Entity entity) + { + base.Removed(entity); + cached.Push(this); + } + + public void Start() + { + Active = true; + TimeLeft = Duration; + } + + public void Start(float duration) + { +#if DEBUG + if (duration <= 0) + throw new Exception("Alarm duration cannot be <= 0"); +#endif + + Duration = duration; + Start(); + } + + public void Stop() + { + Active = false; + } + } +} diff --git a/MonocleEngineDemo/Monocle/Components/Logic/Coroutine.cs b/MonocleEngineDemo/Monocle/Components/Logic/Coroutine.cs new file mode 100644 index 0000000..1bb1215 --- /dev/null +++ b/MonocleEngineDemo/Monocle/Components/Logic/Coroutine.cs @@ -0,0 +1,84 @@ +using System.Collections; +using System.Collections.Generic; + +namespace Monocle +{ + public class Coroutine : Component + { + public bool Finished { get; private set; } + public bool RemoveOnComplete = true; + public bool UseRawDeltaTime = false; + + private Stack enumerators; + private float waitTimer; + private bool ended; + + public Coroutine(IEnumerator functionCall, bool removeOnComplete = true) + : base(true, false) + { + enumerators = new Stack(); + enumerators.Push(functionCall); + RemoveOnComplete = removeOnComplete; + } + + public Coroutine(bool removeOnComplete = true) + : base(false, false) + { + RemoveOnComplete = removeOnComplete; + enumerators = new Stack(); + } + + public override void Update() + { + ended = false; + + if (waitTimer > 0) + waitTimer -= (UseRawDeltaTime ? Engine.RawDeltaTime : Engine.DeltaTime); + else if (enumerators.Count > 0) + { + IEnumerator now = enumerators.Peek(); + if (now.MoveNext() && !ended) + { + if (now.Current is int) + waitTimer = (int)now.Current; + if (now.Current is float) + waitTimer = (float)now.Current; + else if (now.Current is IEnumerator) + enumerators.Push(now.Current as IEnumerator); + } + else if (!ended) + { + enumerators.Pop(); + if (enumerators.Count == 0) + { + Finished = true; + Active = false; + if (RemoveOnComplete) + RemoveSelf(); + } + } + } + } + + public void Cancel() + { + Active = false; + Finished = true; + waitTimer = 0; + enumerators.Clear(); + + ended = true; + } + + public void Replace(IEnumerator functionCall) + { + Active = true; + Finished = false; + waitTimer = 0; + enumerators.Clear(); + enumerators.Push(functionCall); + + ended = true; + } + } +} diff --git a/MonocleEngineDemo/Monocle/Components/Logic/CoroutineHolder.cs b/MonocleEngineDemo/Monocle/Components/Logic/CoroutineHolder.cs new file mode 100644 index 0000000..a543427 --- /dev/null +++ b/MonocleEngineDemo/Monocle/Components/Logic/CoroutineHolder.cs @@ -0,0 +1,90 @@ +using System.Collections; +using System.Collections.Generic; + +namespace Monocle +{ + public class CoroutineHolder : Component + { + private List coroutineList; + private HashSet toRemove; + private int nextID; + private bool isRunning; + + public CoroutineHolder() + : base(true, false) + { + coroutineList = new List(); + toRemove = new HashSet(); + } + + public override void Update() + { + isRunning = true; + for (int i = 0; i < coroutineList.Count; i++) + { + var now = coroutineList[i].Data.Peek(); + + if (now.MoveNext()) + { + if (now.Current is IEnumerator) + coroutineList[i].Data.Push(now.Current as IEnumerator); + } + else + { + coroutineList[i].Data.Pop(); + if (coroutineList[i].Data.Count == 0) + toRemove.Add(coroutineList[i]); + } + } + isRunning = false; + + if (toRemove.Count > 0) + { + foreach (var r in toRemove) + coroutineList.Remove(r); + toRemove.Clear(); + } + } + + public void EndCoroutine(int id) + { + foreach (var c in coroutineList) + { + if (c.ID == id) + { + if (isRunning) + toRemove.Add(c); + else + coroutineList.Remove(c); + break; + } + } + } + + public int StartCoroutine(IEnumerator functionCall) + { + var data = new CoroutineData(nextID++, functionCall); + coroutineList.Add(data); + return data.ID; + } + + public static IEnumerator WaitForFrames(int frames) + { + for (int i = 0; i < frames; i++) + yield return 0; + } + + private class CoroutineData + { + public int ID; + public Stack Data; + + public CoroutineData(int id, IEnumerator functionCall) + { + ID = id; + Data = new Stack(); + Data.Push(functionCall); + } + } + } +} diff --git a/MonocleEngineDemo/Monocle/Components/Logic/CounterSet.cs b/MonocleEngineDemo/Monocle/Components/Logic/CounterSet.cs new file mode 100644 index 0000000..2cbbe60 --- /dev/null +++ b/MonocleEngineDemo/Monocle/Components/Logic/CounterSet.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Monocle +{ + public class CounterSet : Component + { + private Dictionary counters; + private float timer; + + public CounterSet() + : base(true, false) + { + counters = new Dictionary(); + } + + public float this[T index] + { + get + { + float value; + if (counters.TryGetValue(index, out value)) + return Math.Max(value - timer, 0); + else + return 0; + } + + set + { + counters[index] = timer + value; + } + } + + public bool Check(T index) + { + float value; + if (counters.TryGetValue(index, out value)) + return value - timer > 0; + else + return false; + } + + public override void Update() + { + timer += Engine.DeltaTime; + } + } +} diff --git a/MonocleEngineDemo/Monocle/Components/Logic/Shaker.cs b/MonocleEngineDemo/Monocle/Components/Logic/Shaker.cs new file mode 100644 index 0000000..82524c6 --- /dev/null +++ b/MonocleEngineDemo/Monocle/Components/Logic/Shaker.cs @@ -0,0 +1,90 @@ +using Microsoft.Xna.Framework; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Monocle +{ + public class Shaker : Component + { + public Vector2 Value; + public float Interval = .05f; + public float Timer; + public bool RemoveOnFinish; + public Action OnShake; + + private bool on; + + public Shaker(bool on = true, Action onShake = null) + : base(true, false) + { + this.on = on; + OnShake = onShake; + } + + public Shaker(float time, bool removeOnFinish, Action onShake = null) + : this(true, onShake) + { + Timer = time; + RemoveOnFinish = removeOnFinish; + } + + public bool On + { + get + { + return on; + } + + set + { + on = value; + if (!on) + { + Timer = 0; + if (Value != Vector2.Zero) + { + Value = Vector2.Zero; + if (OnShake != null) + OnShake(Vector2.Zero); + } + } + } + } + + public Shaker ShakeFor(float seconds, bool removeOnFinish) + { + on = true; + Timer = seconds; + RemoveOnFinish = removeOnFinish; + + return this; + } + + public override void Update() + { + if (on && Timer > 0) + { + Timer -= Engine.DeltaTime; + if (Timer <= 0) + { + on = false; + Value = Vector2.Zero; + if (OnShake != null) + OnShake(Vector2.Zero); + if (RemoveOnFinish) + RemoveSelf(); + return; + } + } + + if (on && Scene.OnInterval(Interval)) + { + Value = Calc.Random.ShakeVector(); + if (OnShake != null) + OnShake(Value); + } + } + } +} diff --git a/MonocleEngineDemo/Monocle/Components/Logic/ShakerList.cs b/MonocleEngineDemo/Monocle/Components/Logic/ShakerList.cs new file mode 100644 index 0000000..be44235 --- /dev/null +++ b/MonocleEngineDemo/Monocle/Components/Logic/ShakerList.cs @@ -0,0 +1,94 @@ +using Microsoft.Xna.Framework; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Monocle +{ + public class ShakerList : Component + { + public Vector2[] Values; + public float Interval = .05f; + public float Timer; + public bool RemoveOnFinish; + public Action OnShake; + + private bool on; + + public ShakerList(int length, bool on = true, Action onShake = null) + : base(true, false) + { + Values = new Vector2[length]; + this.on = on; + OnShake = onShake; + } + + public ShakerList(int length, float time, bool removeOnFinish, Action onShake = null) + : this(length, true, onShake) + { + Timer = time; + RemoveOnFinish = removeOnFinish; + } + + public bool On + { + get + { + return on; + } + + set + { + on = value; + if (!on) + { + Timer = 0; + if (Values[0] != Vector2.Zero) + { + for (var i = 0; i < Values.Length; i++) + Values[i] = Vector2.Zero; + if (OnShake != null) + OnShake(Values); + } + } + } + } + + public ShakerList ShakeFor(float seconds, bool removeOnFinish) + { + on = true; + Timer = seconds; + RemoveOnFinish = removeOnFinish; + + return this; + } + + public override void Update() + { + if (on && Timer > 0) + { + Timer -= Engine.DeltaTime; + if (Timer <= 0) + { + on = false; + for (var i = 0; i < Values.Length; i++) + Values[i] = Vector2.Zero; + if (OnShake != null) + OnShake(Values); + if (RemoveOnFinish) + RemoveSelf(); + return; + } + } + + if (on && Scene.OnInterval(Interval)) + { + for (var i = 0; i < Values.Length; i++) + Values[i] = Calc.Random.ShakeVector(); + if (OnShake != null) + OnShake(Values); + } + } + } +} diff --git a/MonocleEngineDemo/Monocle/Components/Logic/SineWave.cs b/MonocleEngineDemo/Monocle/Components/Logic/SineWave.cs new file mode 100644 index 0000000..e44eb6f --- /dev/null +++ b/MonocleEngineDemo/Monocle/Components/Logic/SineWave.cs @@ -0,0 +1,107 @@ +using Microsoft.Xna.Framework; +using System; + +namespace Monocle +{ + public class SineWave : Component + { + /* + * SINE WAVE: + * + * 1 x + * | x x + * | x x + * | x x + * -x-------------x-------------x + * | x x + * | x x + * | x x + * -1 x + * + * COS WAVE: + * + * 1x x + * | x x + * | x x + * | x x + * --------x-------------x------- + * | x x + * | x x + * | x x + * -1 x + * + */ + + public float Frequency = 1f; + public float Rate = 1f; + public float Value { get; private set; } + public float ValueOverTwo { get; private set; } + public float TwoValue { get; private set; } + public Action OnUpdate; + public bool UseRawDeltaTime; + + private float counter; + + public SineWave() + : base(true, false) + { + + } + + public SineWave(float frequency) + : this() + { + Frequency = frequency; + } + + public override void Update() + { + Counter += MathHelper.TwoPi * Frequency * Rate * (UseRawDeltaTime ? Engine.RawDeltaTime : Engine.DeltaTime); + if (OnUpdate != null) + OnUpdate(Value); + } + + public float ValueOffset(float offset) + { + return (float)Math.Sin(counter + offset); + } + + public SineWave Randomize() + { + Counter = Calc.Random.NextFloat() * MathHelper.TwoPi * 2; + return this; + } + + public void Reset() + { + Counter = 0; + } + + public void StartUp() + { + Counter = MathHelper.PiOver2; + } + + public void StartDown() + { + Counter = MathHelper.PiOver2 * 3f; + } + + public float Counter + { + get + { + return counter; + } + + set + { + counter = (value + MathHelper.TwoPi * 4) % (MathHelper.TwoPi * 4); + + Value = (float)Math.Sin(counter); + ValueOverTwo = (float)Math.Sin(counter / 2); + TwoValue = (float)Math.Sin(counter * 2); + } + } + } +} diff --git a/MonocleEngineDemo/Monocle/Components/Logic/StateMachine.cs b/MonocleEngineDemo/Monocle/Components/Logic/StateMachine.cs new file mode 100644 index 0000000..b2ae3c3 --- /dev/null +++ b/MonocleEngineDemo/Monocle/Components/Logic/StateMachine.cs @@ -0,0 +1,183 @@ +using System; +using System.Collections; + +namespace Monocle +{ + public class StateMachine : Component + { + private int state; + private Action[] begins; + private Func[] updates; + private Action[] ends; + private Func[] coroutines; + private Coroutine currentCoroutine; + + public bool ChangedStates; + public bool Log; + public int PreviousState { get; private set; } + public bool Locked; + + public StateMachine(int maxStates = 10) + : base(true, false) + { + PreviousState = state = -1; + + begins = new Action[maxStates]; + updates = new Func[maxStates]; + ends = new Action[maxStates]; + coroutines = new Func[maxStates]; + + currentCoroutine = new Coroutine(); + currentCoroutine.RemoveOnComplete = false; + } + + public override void Added(Entity entity) + { + base.Added(entity); + + if (Entity.Scene != null && state == -1) + State = 0; + } + + public override void EntityAdded(Scene scene) + { + base.EntityAdded(scene); + + if (state == -1) + State = 0; + } + + public int State + { + get { return state; } + set + { +#if DEBUG + if (value >= updates.Length || value < 0) + throw new Exception("StateMachine state out of range"); +#endif + + if (!Locked && state != value) + { + if (Log) + Calc.Log("Enter State " + value + " (leaving " + state + ")"); + + ChangedStates = true; + PreviousState = state; + state = value; + + if (PreviousState != -1 && ends[PreviousState] != null) + { + if (Log) + Calc.Log("Calling End " + PreviousState); + ends[PreviousState](); + } + + if (begins[state] != null) + { + if (Log) + Calc.Log("Calling Begin " + state); + begins[state](); + } + + if (coroutines[state] != null) + { + if (Log) + Calc.Log("Starting Coroutine " + state); + currentCoroutine.Replace(coroutines[state]()); + } + else + currentCoroutine.Cancel(); + } + } + } + + public void ForceState(int toState) + { + if (state != toState) + State = toState; + else + { + if (Log) + Calc.Log("Enter State " + toState + " (leaving " + state + ")"); + + ChangedStates = true; + PreviousState = state; + state = toState; + + if (PreviousState != -1 && ends[PreviousState] != null) + { + if (Log) + Calc.Log("Calling End " + state); + ends[PreviousState](); + } + + if (begins[state] != null) + { + if (Log) + Calc.Log("Calling Begin " + state); + begins[state](); + } + + if (coroutines[state] != null) + { + if (Log) + Calc.Log("Starting Coroutine " + state); + currentCoroutine.Replace(coroutines[state]()); + } + else + currentCoroutine.Cancel(); + } + } + + public void SetCallbacks(int state, Func onUpdate, Func coroutine = null, Action begin = null, Action end = null) + { + updates[state] = onUpdate; + begins[state] = begin; + ends[state] = end; + coroutines[state] = coroutine; + } + + public void ReflectState(Entity from, int index, string name) + { + updates[index] = (Func)Calc.GetMethod>(from, name + "Update"); + begins[index] = (Action)Calc.GetMethod(from, name + "Begin"); + ends[index] = (Action)Calc.GetMethod(from, name + "End"); + coroutines[index] = (Func)Calc.GetMethod>(from, name + "Coroutine"); + } + + public override void Update() + { + ChangedStates = false; + + if (updates[state] != null) + State = updates[state](); + if (currentCoroutine.Active) + { + currentCoroutine.Update(); + if (!ChangedStates && Log && currentCoroutine.Finished) + Calc.Log("Finished Coroutine " + state); + } + } + + public static implicit operator int(StateMachine s) + { + return s.state; + } + + public void LogAllStates() + { + for (int i = 0; i < updates.Length; i++) + LogState(i); + } + + public void LogState(int index) + { + Calc.Log("State " + index + ": " + + (updates[index] != null ? "U" : "") + + (begins[index] != null ? "B" : "") + + (ends[index] != null ? "E" : "") + + (coroutines[index] != null ? "C" : "")); + } + } +} diff --git a/MonocleEngineDemo/Monocle/Components/Logic/Tween.cs b/MonocleEngineDemo/Monocle/Components/Logic/Tween.cs new file mode 100644 index 0000000..e69741d --- /dev/null +++ b/MonocleEngineDemo/Monocle/Components/Logic/Tween.cs @@ -0,0 +1,213 @@ +using Microsoft.Xna.Framework; +using System; +using System.Collections; +using System.Collections.Generic; + +namespace Monocle +{ + public class Tween : Component + { + public enum TweenMode { Persist, Oneshot, Looping, YoyoOneshot, YoyoLooping }; + + public Ease.Easer Easer; + public Action OnUpdate; + public Action OnComplete; + public Action OnStart; + public bool UseRawDeltaTime; + + public TweenMode Mode { get; private set; } + public float Duration { get; private set; } + public float TimeLeft { get; private set; } + public float Percent { get; private set; } + public float Eased { get; private set; } + public bool Reverse { get; private set; } + + private bool startedReversed; + + #region Static + + private static Stack cached = new Stack(); + + public static Tween Create(TweenMode mode, Ease.Easer easer = null, float duration = 1f, bool start = false) + { + Tween tween; + if (cached.Count == 0) + tween = new Tween(); + else + tween = cached.Pop(); + tween.OnUpdate = tween.OnComplete = tween.OnStart = null; + + tween.Init(mode, easer, duration, start); + return tween; + } + + public static Tween Set(Entity entity, TweenMode tweenMode, float duration, Ease.Easer easer, Action onUpdate, Action onComplete = null) + { + Tween tween = Tween.Create(tweenMode, easer, duration, true); + tween.OnUpdate += onUpdate; + tween.OnComplete += onComplete; + entity.Add(tween); + return tween; + } + + public static Tween Position(Entity entity, Vector2 targetPosition, float duration, Ease.Easer easer, TweenMode tweenMode = TweenMode.Oneshot) + { + Vector2 startPosition = entity.Position; + Tween tween = Tween.Create(tweenMode, easer, duration, true); + tween.OnUpdate = (t) => { entity.Position = Vector2.Lerp(startPosition, targetPosition, t.Eased); }; + entity.Add(tween); + return tween; + } + + #endregion + + private Tween() + : base(false, false) + { + + } + + private void Init(TweenMode mode, Ease.Easer easer, float duration, bool start) + { +#if DEBUG + if (duration <= 0) + throw new Exception("Tween duration cannot be less than zero"); +#else + if (duration <= 0) + duration = .000001f; +#endif + + UseRawDeltaTime = false; + Mode = mode; + Easer = easer; + Duration = duration; + + TimeLeft = 0; + Percent = 0; + Active = false; + + if (start) + Start(); + } + + public override void Removed(Entity entity) + { + base.Removed(entity); + cached.Push(this); + } + + public override void Update() + { + TimeLeft -= (UseRawDeltaTime ? Engine.RawDeltaTime : Engine.DeltaTime); + + //Update the percentage and eased percentage + Percent = Math.Max(0, TimeLeft) / (float)Duration; + if (!Reverse) + Percent = 1 - Percent; + if (Easer != null) + Eased = Easer(Percent); + else + Eased = Percent; + + //Update the tween + if (OnUpdate != null) + OnUpdate(this); + + //When finished... + if (TimeLeft <= 0) + { + TimeLeft = 0; + + if (OnComplete != null) + OnComplete(this); + + switch (Mode) + { + case TweenMode.Persist: + Active = false; + break; + + case TweenMode.Oneshot: + Active = false; + RemoveSelf(); + break; + + case TweenMode.Looping: + Start(Reverse); + break; + + case TweenMode.YoyoOneshot: + if (Reverse == startedReversed) + { + Start(!Reverse); + startedReversed = !Reverse; + } + else + { + Active = false; + RemoveSelf(); + } + break; + + case TweenMode.YoyoLooping: + Start(!Reverse); + break; + } + } + } + + public void Start() + { + Start(false); + } + + public void Start(bool reverse) + { + startedReversed = Reverse = reverse; + + TimeLeft = Duration; + Eased = Percent = Reverse ? 1 : 0; + + Active = true; + + if (OnStart != null) + OnStart(this); + } + + public void Start(float duration, bool reverse = false) + { +#if DEBUG + if (duration <= 0) + throw new Exception("Tween duration cannot be <= 0"); +#endif + + Duration = duration; + Start(reverse); + } + + public void Stop() + { + Active = false; + } + + public void Reset() + { + TimeLeft = Duration; + Eased = Percent = Reverse ? 1 : 0; + } + + public IEnumerator Wait() + { + while (Active) + yield return 0; + } + + public float Inverted + { + get + { + return 1f - Eased; + } + } + } +} diff --git a/MonocleEngineDemo/Monocle/Components/Logic/Wiggler.cs b/MonocleEngineDemo/Monocle/Components/Logic/Wiggler.cs new file mode 100644 index 0000000..00a183a --- /dev/null +++ b/MonocleEngineDemo/Monocle/Components/Logic/Wiggler.cs @@ -0,0 +1,131 @@ +using Microsoft.Xna.Framework; +using System; +using System.Collections.Generic; + +namespace Monocle +{ + public class Wiggler : Component + { + private static Stack cache = new Stack(); + + public float Counter { get; private set; } + public float Value { get; private set; } + public bool StartZero; + public bool UseRawDeltaTime; + + private float sineCounter; + + private float increment; + private float sineAdd; + private Action onChange; + private bool removeSelfOnFinish; + + public static Wiggler Create(float duration, float frequency, Action onChange = null, bool start = false, bool removeSelfOnFinish = false) + { + Wiggler wiggler; + + if (cache.Count > 0) + wiggler = cache.Pop(); + else + wiggler = new Wiggler(); + wiggler.Init(duration, frequency, onChange, start, removeSelfOnFinish); + + return wiggler; + } + + private Wiggler() + : base(false, false) + { + + } + + private void Init(float duration, float frequency, Action onChange, bool start, bool removeSelfOnFinish) + { + Counter = sineCounter = 0; + UseRawDeltaTime = false; + + increment = 1f / duration; + sineAdd = MathHelper.TwoPi * frequency; + this.onChange = onChange; + this.removeSelfOnFinish = removeSelfOnFinish; + + if (start) + Start(); + else + Active = false; + } + + public override void Removed(Entity entity) + { + base.Removed(entity); + cache.Push(this); + } + + public void Start() + { + Counter = 1f; + + if (StartZero) + { + sineCounter = MathHelper.PiOver2; + Value = 0; + if (onChange != null) + onChange(0); + } + else + { + sineCounter = 0; + Value = 1f; + if (onChange != null) + onChange(1f); + } + + Active = true; + } + + public void Start(float duration, float frequency) + { + increment = 1f / duration; + sineAdd = MathHelper.TwoPi * frequency; + Start(); + } + + public void Stop() + { + Active = false; + } + + public void StopAndClear() + { + Stop(); + Value = 0; + } + + public override void Update() + { + if (UseRawDeltaTime) + { + sineCounter += sineAdd * Engine.RawDeltaTime; + Counter -= increment * Engine.RawDeltaTime; + } + else + { + sineCounter += sineAdd * Engine.DeltaTime; + Counter -= increment * Engine.DeltaTime; + } + + if (Counter <= 0) + { + Counter = 0; + Active = false; + if (removeSelfOnFinish) + RemoveSelf(); + } + + Value = (float)Math.Cos(sineCounter) * Counter; + + if (onChange != null) + onChange(Value); + } + } +} diff --git a/MonocleEngineDemo/Monocle/Content/Monocle/MonocleDefault.spritefont b/MonocleEngineDemo/Monocle/Content/Monocle/MonocleDefault.spritefont new file mode 100644 index 0000000..95035fa --- /dev/null +++ b/MonocleEngineDemo/Monocle/Content/Monocle/MonocleDefault.spritefont @@ -0,0 +1,60 @@ + + + + + + + Consolas + + + 12 + + + 0 + + + true + + + + + + + + + + + + ~ + + + + diff --git a/MonocleEngineDemo/Monocle/Content/Monocle/MonocleDefault.xnb b/MonocleEngineDemo/Monocle/Content/Monocle/MonocleDefault.xnb new file mode 100644 index 0000000000000000000000000000000000000000..ce83ae5b3a2055feaeb427a2386b511fb9f28b11 GIT binary patch literal 21678 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 0 && Window.ClientBounds.Height > 0 && !resizing) + { + resizing = true; + + Graphics.PreferredBackBufferWidth = Window.ClientBounds.Width; + Graphics.PreferredBackBufferHeight = Window.ClientBounds.Height; + UpdateView(); + + resizing = false; + } + } +#endif + + protected virtual void OnGraphicsReset(object sender, EventArgs e) + { + UpdateView(); + + if (scene != null) + scene.HandleGraphicsReset(); + if (nextScene != null && nextScene != scene) + nextScene.HandleGraphicsReset(); + } + + protected virtual void OnGraphicsCreate(object sender, EventArgs e) + { + UpdateView(); + + if (scene != null) + scene.HandleGraphicsCreate(); + if (nextScene != null && nextScene != scene) + nextScene.HandleGraphicsCreate(); + } + + protected override void OnActivated(object sender, EventArgs args) + { + base.OnActivated(sender, args); + + if (scene != null) + scene.GainFocus(); + } + + protected override void OnDeactivated(object sender, EventArgs args) + { + base.OnDeactivated(sender, args); + + if (scene != null) + scene.LoseFocus(); + } + + protected override void Initialize() + { + base.Initialize(); + + MInput.Initialize(); + Tracker.Initialize(); + Pooler = new Monocle.Pooler(); + Commands = new Commands(); + } + + protected override void LoadContent() + { + base.LoadContent(); + + Monocle.Draw.Initialize(GraphicsDevice); + } + + protected override void Update(GameTime gameTime) + { + RawDeltaTime = (float)gameTime.ElapsedGameTime.TotalSeconds; + DeltaTime = RawDeltaTime * TimeRate; + + //Update input + MInput.Update(); + +#if !CONSOLE + if (ExitOnEscapeKeypress && MInput.Keyboard.Pressed(Microsoft.Xna.Framework.Input.Keys.Escape)) + { + Exit(); + return; + } +#endif + + if (OverloadGameLoop != null) + { + OverloadGameLoop(); + base.Update(gameTime); + return; + } + + //Update current scene + if (FreezeTimer > 0) + FreezeTimer = Math.Max(FreezeTimer - RawDeltaTime, 0); + else if (scene != null) + { + scene.BeforeUpdate(); + scene.Update(); + scene.AfterUpdate(); + } + + //Debug Console + if (Commands.Open) + Commands.UpdateOpen(); + else if (Commands.Enabled) + Commands.UpdateClosed(); + + //Changing scenes + if (scene != nextScene) + { + var lastScene = scene; + if (scene != null) + scene.End(); + scene = nextScene; + OnSceneTransition(lastScene, nextScene); + if (scene != null) + scene.Begin(); + } + + base.Update(gameTime); + } + + protected override void Draw(GameTime gameTime) + { + RenderCore(); + + base.Draw(gameTime); + if (Commands.Open) + Commands.Render(); + + //Frame counter + fpsCounter++; + counterElapsed += gameTime.ElapsedGameTime; + if (counterElapsed >= TimeSpan.FromSeconds(1)) + { +#if DEBUG + Window.Title = Title + " " + fpsCounter.ToString() + " fps - " + (GC.GetTotalMemory(false) / 1048576f).ToString("F") + " MB"; +#endif + FPS = fpsCounter; + fpsCounter = 0; + counterElapsed -= TimeSpan.FromSeconds(1); + } + } + + /// + /// Override if you want to change the core rendering functionality of Monocle Engine. + /// By default, this simply sets the render target to null, clears the screen, and renders the current Scene + /// + protected virtual void RenderCore() + { + if (scene != null) + scene.BeforeRender(); + + GraphicsDevice.SetRenderTarget(null); + GraphicsDevice.Viewport = Viewport; + GraphicsDevice.Clear(ClearColor); + + if (scene != null) + { + scene.Render(); + scene.AfterRender(); + } + } + + protected override void OnExiting(object sender, EventArgs args) + { + base.OnExiting(sender, args); + MInput.Shutdown(); + } + + public void RunWithLogging() + { + try + { + Run(); + } + catch (Exception e) + { + ErrorLog.Write(e); + ErrorLog.Open(); + } + } + + #region Scene + + /// + /// Called after a Scene ends, before the next Scene begins + /// + protected virtual void OnSceneTransition(Scene from, Scene to) + { + GC.Collect(); + GC.WaitForPendingFinalizers(); + + TimeRate = 1f; + } + + /// + /// The currently active Scene. Note that if set, the Scene will not actually change until the end of the Update + /// + public static Scene Scene + { + get { return Instance.scene; } + set { Instance.nextScene = value; } + } + + #endregion + + #region Screen + + public static Viewport Viewport { get; private set; } + public static Matrix ScreenMatrix; + + public static void SetWindowed(int width, int height) + { +#if !CONSOLE + if (width > 0 && height > 0) + { + resizing = true; + Graphics.PreferredBackBufferWidth = width; + Graphics.PreferredBackBufferHeight = height; + Graphics.IsFullScreen = false; + Graphics.ApplyChanges(); + Console.WriteLine("WINDOW-" + width + "x" + height); + resizing = false; + } +#endif + } + + public static void SetFullscreen() + { +#if !CONSOLE + resizing = true; + Graphics.PreferredBackBufferWidth = GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Width; + Graphics.PreferredBackBufferHeight = GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Height; + Graphics.IsFullScreen = true; + Graphics.ApplyChanges(); + Console.WriteLine("FULLSCREEN"); + resizing = false; +#endif + } + + private void UpdateView() + { + float screenWidth = GraphicsDevice.PresentationParameters.BackBufferWidth; + float screenHeight = GraphicsDevice.PresentationParameters.BackBufferHeight; + + // get View Size + if (screenWidth / Width > screenHeight / Height) + { + ViewWidth = (int)(screenHeight / Height * Width); + ViewHeight = (int)screenHeight; + } + else + { + ViewWidth = (int)screenWidth; + ViewHeight = (int)(screenWidth / Width * Height); + } + + // apply View Padding + var aspect = ViewHeight / (float)ViewWidth; + ViewWidth -= ViewPadding * 2; + ViewHeight -= (int)(aspect * ViewPadding * 2); + + // update screen matrix + ScreenMatrix = Matrix.CreateScale(ViewWidth / (float)Width); + + // update viewport + Viewport = new Viewport + { + X = (int)(screenWidth / 2 - ViewWidth / 2), + Y = (int)(screenHeight / 2 - ViewHeight / 2), + Width = ViewWidth, + Height = ViewHeight, + MinDepth = 0, + MaxDepth = 1 + }; + + //Debug Log + //Calc.Log("Update View - " + screenWidth + "x" + screenHeight + " - " + viewport.Width + "x" + viewport.GuiHeight + " - " + viewport.X + "," + viewport.Y); + } + + #endregion + } +} diff --git a/MonocleEngineDemo/Monocle/Entity.cs b/MonocleEngineDemo/Monocle/Entity.cs new file mode 100644 index 0000000..7176c7b --- /dev/null +++ b/MonocleEngineDemo/Monocle/Entity.cs @@ -0,0 +1,1170 @@ +using Microsoft.Xna.Framework; +using System; +using System.Collections; +using System.Collections.Generic; + +namespace Monocle +{ + public class Entity : IEnumerable, IEnumerable + { + public bool Active = true; + public bool Visible = true; + public bool Collidable = true; + public Vector2 Position; + + public Scene Scene { get; private set; } + public ComponentList Components { get; private set; } + + private int tag; + private Collider collider; + internal int depth = 0; + internal double actualDepth = 0; + + public Entity(Vector2 position) + { + Position = position; + Components = new ComponentList(this); + } + + public Entity() + : this(Vector2.Zero) + { + + } + + /// + /// Called when the containing Scene Begins + /// + public virtual void SceneBegin(Scene scene) + { + + } + + /// + /// Called when the containing Scene Ends + /// + public virtual void SceneEnd(Scene scene) + { + if (Components != null) + foreach (var c in Components) + c.SceneEnd(scene); + } + + /// + /// Called before the frame starts, after Entities are added and removed, on the frame that the Entity was added + /// Useful if you added two Entities in the same frame, and need them to detect each other before they start Updating + /// + /// + public virtual void Awake(Scene scene) + { + if (Components != null) + foreach (var c in Components) + c.EntityAwake(); + } + + /// + /// Called when this Entity is added to a Scene, which only occurs immediately before each Update. + /// Keep in mind, other Entities to be added this frame may be added after this Entity. + /// See Awake() for after all Entities are added, but still before the frame Updates. + /// + /// + public virtual void Added(Scene scene) + { + Scene = scene; + if (Components != null) + foreach (var c in Components) + c.EntityAdded(scene); + Scene.SetActualDepth(this); + } + + /// + /// Called when the Entity is removed from a Scene + /// + /// + public virtual void Removed(Scene scene) + { + if (Components != null) + foreach (var c in Components) + c.EntityRemoved(scene); + Scene = null; + } + + /// + /// Do game logic here, but do not render here. Not called if the Entity is not Active + /// + public virtual void Update() + { + Components.Update(); + } + + /// + /// Draw the Entity here. Not called if the Entity is not Visible + /// + public virtual void Render() + { + Components.Render(); + } + + /// + /// Draw any debug visuals here. Only called if the console is open, but still called even if the Entity is not Visible + /// + public virtual void DebugRender(Camera camera) + { + if (Collider != null) + Collider.Render(camera, Collidable ? Color.Red : Color.DarkRed); + + Components.DebugRender(camera); + } + + /// + /// Called when the graphics device resets. When this happens, any RenderTargets or other contents of VRAM will be wiped and need to be regenerated + /// + public virtual void HandleGraphicsReset() + { + Components.HandleGraphicsReset(); + } + + public virtual void HandleGraphicsCreate() + { + Components.HandleGraphicsCreate(); + } + + public void RemoveSelf() + { + if (Scene != null) + Scene.Entities.Remove(this); + } + + public int Depth + { + get { return depth; } + set + { + if (depth != value) + { + depth = value; + if (Scene != null) + Scene.SetActualDepth(this); + } + } + } + + public float X + { + get { return Position.X; } + set { Position.X = value; } + } + + public float Y + { + get { return Position.Y; } + set { Position.Y = value; } + } + + #region Collider + + public Collider Collider + { + get { return collider; } + set + { + if (value == collider) + return; +#if DEBUG + if (value.Entity != null) + throw new Exception("Setting an Entity's Collider to a Collider already in use by another object"); +#endif + if (collider != null) + collider.Removed(); + collider = value; + if (collider != null) + collider.Added(this); + } + } + + public float Width + { + get + { + if (collider == null) + return 0; + else + return collider.Width; + } + } + + public float Height + { + get + { + if (collider == null) + return 0; + else + return collider.Height; + } + } + + public float Left + { + get + { + if (collider == null) + return X; + else + return Position.X + collider.Left; + } + + set + { + if (collider == null) + Position.X = value; + else + Position.X = value - collider.Left; + } + } + + public float Right + { + get + { + if (collider == null) + return Position.X; + else + return Position.X + collider.Right; + } + + set + { + if (collider == null) + Position.X = value; + else + Position.X = value - collider.Right; + } + } + + public float Top + { + get + { + if (collider == null) + return Position.Y; + else + return Position.Y + collider.Top; + } + + set + { + if (collider == null) + Position.Y = value; + else + Position.Y = value - collider.Top; + } + } + + public float Bottom + { + get + { + if (collider == null) + return Position.Y; + else + return Position.Y + collider.Bottom; + } + + set + { + if (collider == null) + Position.Y = value; + else + Position.Y = value - collider.Bottom; + } + } + + public float CenterX + { + get + { + if (collider == null) + return Position.X; + else + return Position.X + collider.CenterX; + } + + set + { + if (collider == null) + Position.X = value; + else + Position.X = value - collider.CenterX; + } + } + + public float CenterY + { + get + { + if (collider == null) + return Position.Y; + else + return Position.Y + collider.CenterY; + } + + set + { + if (collider == null) + Position.Y = value; + else + Position.Y = value - collider.CenterY; + } + } + + public Vector2 TopLeft + { + get + { + return new Vector2(Left, Top); + } + + set + { + Left = value.X; + Top = value.Y; + } + } + + public Vector2 TopRight + { + get + { + return new Vector2(Right, Top); + } + + set + { + Right = value.X; + Top = value.Y; + } + } + + public Vector2 BottomLeft + { + get + { + return new Vector2(Left, Bottom); + } + + set + { + Left = value.X; + Bottom = value.Y; + } + } + + public Vector2 BottomRight + { + get + { + return new Vector2(Right, Bottom); + } + + set + { + Right = value.X; + Bottom = value.Y; + } + } + + public Vector2 Center + { + get + { + return new Vector2(CenterX, CenterY); + } + + set + { + CenterX = value.X; + CenterY = value.Y; + } + } + + public Vector2 CenterLeft + { + get + { + return new Vector2(Left, CenterY); + } + + set + { + Left = value.X; + CenterY = value.Y; + } + } + + public Vector2 CenterRight + { + get + { + return new Vector2(Right, CenterY); + } + + set + { + Right = value.X; + CenterY = value.Y; + } + } + + public Vector2 TopCenter + { + get + { + return new Vector2(CenterX, Top); + } + + set + { + CenterX = value.X; + Top = value.Y; + } + } + + public Vector2 BottomCenter + { + get + { + return new Vector2(CenterX, Bottom); + } + + set + { + CenterX = value.X; + Bottom = value.Y; + } + } + + #endregion + + #region Tag + + public int Tag + { + get + { + return tag; + } + + set + { + if (tag != value) + { + if (Scene != null) + { + for (int i = 0; i < Monocle.BitTag.TotalTags; i++) + { + int check = 1 << i; + bool add = (value & check) != 0; + bool has = (Tag & check) != 0; + + if (has != add) + { + if (add) + Scene.TagLists[i].Add(this); + else + Scene.TagLists[i].Remove(this); + } + } + } + + tag = value; + } + } + } + + public bool TagFullCheck(int tag) + { + return (this.tag & tag) == tag; + } + + public bool TagCheck(int tag) + { + return (this.tag & tag) != 0; + } + + public void AddTag(int tag) + { + Tag |= tag; + } + + public void RemoveTag(int tag) + { + Tag &= ~tag; + } + + #endregion + + #region Collision Shortcuts + + #region Collide Check + + public bool CollideCheck(Entity other) + { + return Collide.Check(this, other); + } + + public bool CollideCheck(Entity other, Vector2 at) + { + return Collide.Check(this, other, at); + } + + public bool CollideCheck(CollidableComponent other) + { + return Collide.Check(this, other); + } + + public bool CollideCheck(CollidableComponent other, Vector2 at) + { + return Collide.Check(this, other, at); + } + + public bool CollideCheck(BitTag tag) + { +#if DEBUG + if (Scene == null) + throw new Exception("Can't collide check an Entity against a tag list when it is not a member of a Scene"); +#endif + return Collide.Check(this, Scene[tag]); + } + + public bool CollideCheck(BitTag tag, Vector2 at) + { +#if DEBUG + if (Scene == null) + throw new Exception("Can't collide check an Entity against a tag list when it is not a member of a Scene"); +#endif + return Collide.Check(this, Scene[tag], at); + } + + public bool CollideCheck() where T : Entity + { +#if DEBUG + if (Scene == null) + throw new Exception("Can't collide check an Entity against tracked Entities when it is not a member of a Scene"); + else if (!Scene.Tracker.Entities.ContainsKey(typeof(T))) + throw new Exception("Can't collide check an Entity against an untracked Entity type"); +#endif + + return Collide.Check(this, Scene.Tracker.Entities[typeof(T)]); + } + + public bool CollideCheck(Vector2 at) where T : Entity + { + return Collide.Check(this, Scene.Tracker.Entities[typeof(T)], at); + } + + public bool CollideCheck() where T : Entity where Exclude : Entity + { +#if DEBUG + if (Scene == null) + throw new Exception("Can't collide check an Entity against tracked objects when it is not a member of a Scene"); + else if (!Scene.Tracker.Entities.ContainsKey(typeof(T))) + throw new Exception("Can't collide check an Entity against an untracked Entity type"); + else if (!Scene.Tracker.Entities.ContainsKey(typeof(Exclude))) + throw new Exception("Excluded type is an untracked Entity type!"); +#endif + + var exclude = Scene.Tracker.Entities[typeof(Exclude)]; + foreach (var e in Scene.Tracker.Entities[typeof(T)]) + if (!exclude.Contains(e)) + if (Collide.Check(this, e)) + return true; + return false; + } + + public bool CollideCheck(Vector2 at) where T : Entity where Exclude : Entity + { + var was = Position; + Position = at; + var ret = CollideCheck(); + Position = was; + return ret; + } + + public bool CollideCheckByComponent() where T : CollidableComponent + { +#if DEBUG + if (Scene == null) + throw new Exception("Can't collide check an Entity against tracked CollidableComponents when it is not a member of a Scene"); + else if (!Scene.Tracker.Components.ContainsKey(typeof(T))) + throw new Exception("Can't collide check an Entity against an untracked CollidableComponent type"); +#endif + + foreach (var c in Scene.Tracker.CollidableComponents[typeof(T)]) + if (Collide.Check(this, c)) + return true; + return false; + } + + public bool CollideCheckByComponent(Vector2 at) where T : CollidableComponent + { + Vector2 old = Position; + Position = at; + bool ret = CollideCheckByComponent(); + Position = old; + return ret; + } + + #endregion + + #region Collide CheckOutside + + public bool CollideCheckOutside(Entity other, Vector2 at) + { + return !Collide.Check(this, other) && Collide.Check(this, other, at); + } + + public bool CollideCheckOutside(BitTag tag, Vector2 at) + { +#if DEBUG + if (Scene == null) + throw new Exception("Can't collide check an Entity against a tag list when it is not a member of a Scene"); +#endif + + foreach (var entity in Scene[tag]) + if (!Collide.Check(this, entity) && Collide.Check(this, entity, at)) + return true; + + return false; + } + + public bool CollideCheckOutside(Vector2 at) where T : Entity + { +#if DEBUG + if (Scene == null) + throw new Exception("Can't collide check an Entity against tracked Entities when it is not a member of a Scene"); + else if (!Scene.Tracker.Entities.ContainsKey(typeof(T))) + throw new Exception("Can't collide check an Entity against an untracked Entity type"); +#endif + + foreach (var entity in Scene.Tracker.Entities[typeof(T)]) + if (!Collide.Check(this, entity) && Collide.Check(this, entity, at)) + return true; + return false; + } + + public bool CollideCheckOutsideByComponent(Vector2 at) where T : CollidableComponent + { +#if DEBUG + if (Scene == null) + throw new Exception("Can't collide check an Entity against tracked CollidableComponents when it is not a member of a Scene"); + else if (!Scene.Tracker.CollidableComponents.ContainsKey(typeof(T))) + throw new Exception("Can't collide check an Entity against an untracked CollidableComponent type"); +#endif + + foreach (var component in Scene.Tracker.CollidableComponents[typeof(T)]) + if (!Collide.Check(this, component) && Collide.Check(this, component, at)) + return true; + return false; + } + + #endregion + + #region Collide First + + public Entity CollideFirst(BitTag tag) + { +#if DEBUG + if (Scene == null) + throw new Exception("Can't collide check an Entity against a tag list when it is not a member of a Scene"); +#endif + return Collide.First(this, Scene[tag]); + } + + public Entity CollideFirst(BitTag tag, Vector2 at) + { +#if DEBUG + if (Scene == null) + throw new Exception("Can't collide check an Entity against a tag list when it is not a member of a Scene"); +#endif + return Collide.First(this, Scene[tag], at); + } + + public T CollideFirst() where T : Entity + { +#if DEBUG + if (Scene == null) + throw new Exception("Can't collide check an Entity against tracked Entities when it is not a member of a Scene"); + else if (!Scene.Tracker.Entities.ContainsKey(typeof(T))) + throw new Exception("Can't collide check an Entity against an untracked Entity type"); +#endif + return Collide.First(this, Scene.Tracker.Entities[typeof(T)]) as T; + } + + public T CollideFirst(Vector2 at) where T : Entity + { +#if DEBUG + if (Scene == null) + throw new Exception("Can't collide check an Entity against tracked Entities when it is not a member of a Scene"); + else if (!Scene.Tracker.Entities.ContainsKey(typeof(T))) + throw new Exception("Can't collide check an Entity against an untracked Entity type"); +#endif + return Collide.First(this, Scene.Tracker.Entities[typeof(T)], at) as T; + } + + public T CollideFirstByComponent() where T : CollidableComponent + { +#if DEBUG + if (Scene == null) + throw new Exception("Can't collide check an Entity against tracked CollidableComponents when it is not a member of a Scene"); + else if (!Scene.Tracker.CollidableComponents.ContainsKey(typeof(T))) + throw new Exception("Can't collide check an Entity against an untracked CollidableComponent type"); +#endif + + foreach (var component in Scene.Tracker.CollidableComponents[typeof(T)]) + if (Collide.Check(this, component)) + return component as T; + return null; + } + + public T CollideFirstByComponent(Vector2 at) where T : CollidableComponent + { +#if DEBUG + if (Scene == null) + throw new Exception("Can't collide check an Entity against tracked CollidableComponents when it is not a member of a Scene"); + else if (!Scene.Tracker.CollidableComponents.ContainsKey(typeof(T))) + throw new Exception("Can't collide check an Entity against an untracked CollidableComponent type"); +#endif + + foreach (var component in Scene.Tracker.CollidableComponents[typeof(T)]) + if (Collide.Check(this, component, at)) + return component as T; + return null; + } + + #endregion + + #region Collide FirstOutside + + public Entity CollideFirstOutside(BitTag tag, Vector2 at) + { +#if DEBUG + if (Scene == null) + throw new Exception("Can't collide check an Entity against a tag list when it is not a member of a Scene"); +#endif + + foreach (var entity in Scene[tag]) + if (!Collide.Check(this, entity) && Collide.Check(this, entity, at)) + return entity; + return null; + } + + public T CollideFirstOutside(Vector2 at) where T : Entity + { +#if DEBUG + if (Scene == null) + throw new Exception("Can't collide check an Entity against tracked Entities when it is not a member of a Scene"); + else if (!Scene.Tracker.Entities.ContainsKey(typeof(T))) + throw new Exception("Can't collide check an Entity against an untracked Entity type"); +#endif + + foreach (var entity in Scene.Tracker.Entities[typeof(T)]) + if (!Collide.Check(this, entity) && Collide.Check(this, entity, at)) + return entity as T; + return null; + } + + public T CollideFirstOutsideByComponent(Vector2 at) where T : CollidableComponent + { +#if DEBUG + if (Scene == null) + throw new Exception("Can't collide check an Entity against tracked CollidableComponents when it is not a member of a Scene"); + else if (!Scene.Tracker.CollidableComponents.ContainsKey(typeof(T))) + throw new Exception("Can't collide check an Entity against an untracked CollidableComponent type"); +#endif + + foreach (var component in Scene.Tracker.CollidableComponents[typeof(T)]) + if (!Collide.Check(this, component) && Collide.Check(this, component, at)) + return component as T; + return null; + } + + #endregion + + #region Collide All + + public List CollideAll(BitTag tag) + { +#if DEBUG + if (Scene == null) + throw new Exception("Can't collide check an Entity against a tag list when it is not a member of a Scene"); +#endif + return Collide.All(this, Scene[tag]); + } + + public List CollideAll(BitTag tag, Vector2 at) + { +#if DEBUG + if (Scene == null) + throw new Exception("Can't collide check an Entity against a tag list when it is not a member of a Scene"); +#endif + return Collide.All(this, Scene[tag], at); + } + + public List CollideAll() where T : Entity + { +#if DEBUG + if (Scene == null) + throw new Exception("Can't collide check an Entity against tracked Entities when it is not a member of a Scene"); + else if (!Scene.Tracker.Entities.ContainsKey(typeof(T))) + throw new Exception("Can't collide check an Entity against an untracked Entity type"); +#endif + + return Collide.All(this, Scene.Tracker.Entities[typeof(T)]); + } + + public List CollideAll(Vector2 at) where T : Entity + { +#if DEBUG + if (Scene == null) + throw new Exception("Can't collide check an Entity against tracked Entities when it is not a member of a Scene"); + else if (!Scene.Tracker.Entities.ContainsKey(typeof(T))) + throw new Exception("Can't collide check an Entity against an untracked Entity type"); +#endif + + return Collide.All(this, Scene.Tracker.Entities[typeof(T)], at); + } + + public List CollideAll(Vector2 at, List into) where T : Entity + { +#if DEBUG + if (Scene == null) + throw new Exception("Can't collide check an Entity against tracked Entities when it is not a member of a Scene"); + else if (!Scene.Tracker.Entities.ContainsKey(typeof(T))) + throw new Exception("Can't collide check an Entity against an untracked Entity type"); +#endif + + into.Clear(); + return Collide.All(this, Scene.Tracker.Entities[typeof(T)], into, at); + } + + public List CollideAllByComponent() where T : CollidableComponent + { +#if DEBUG + if (Scene == null) + throw new Exception("Can't collide check an Entity against tracked CollidableComponents when it is not a member of a Scene"); + else if (!Scene.Tracker.CollidableComponents.ContainsKey(typeof(T))) + throw new Exception("Can't collide check an Entity against an untracked CollidableComponent type"); +#endif + + List list = new List(); + foreach (var component in Scene.Tracker.CollidableComponents[typeof(T)]) + if (Collide.Check(this, component)) + list.Add(component as T); + return list; + } + + public List CollideAllByComponent(Vector2 at) where T : CollidableComponent + { + Vector2 old = Position; + Position = at; + var ret = CollideAllByComponent(); + Position = old; + return ret; + } + + #endregion + + #region Collide Do + + public bool CollideDo(BitTag tag, Action action) + { +#if DEBUG + if (Scene == null) + throw new Exception("Can't collide check an Entity against a tag list when it is not a member of a Scene"); +#endif + + bool hit = false; + foreach (var other in Scene[tag]) + { + if (CollideCheck(other)) + { + action(other); + hit = true; + } + } + return hit; + } + + public bool CollideDo(BitTag tag, Action action, Vector2 at) + { +#if DEBUG + if (Scene == null) + throw new Exception("Can't collide check an Entity against a tag list when it is not a member of a Scene"); +#endif + + bool hit = false; + var was = Position; + Position = at; + + foreach (var other in Scene[tag]) + { + if (CollideCheck(other)) + { + action(other); + hit = true; + } + } + + Position = was; + return hit; + } + + public bool CollideDo(Action action) where T : Entity + { +#if DEBUG + if (Scene == null) + throw new Exception("Can't collide check an Entity against tracked Entities when it is not a member of a Scene"); + else if (!Scene.Tracker.Entities.ContainsKey(typeof(T))) + throw new Exception("Can't collide check an Entity against an untracked Entity type"); +#endif + + bool hit = false; + foreach (var other in Scene.Tracker.Entities[typeof(T)]) + { + if (CollideCheck(other)) + { + action(other as T); + hit = true; + } + } + return hit; + } + + public bool CollideDo(Action action, Vector2 at) where T : Entity + { +#if DEBUG + if (Scene == null) + throw new Exception("Can't collide check an Entity against tracked Entities when it is not a member of a Scene"); + else if (!Scene.Tracker.Entities.ContainsKey(typeof(T))) + throw new Exception("Can't collide check an Entity against an untracked Entity type"); +#endif + + bool hit = false; + var was = Position; + Position = at; + + foreach (var other in Scene.Tracker.Entities[typeof(T)]) + { + if (CollideCheck(other)) + { + action(other as T); + hit = true; + } + } + + Position = was; + return hit; + } + + public bool CollideDoByComponent(Action action) where T : CollidableComponent + { +#if DEBUG + if (Scene == null) + throw new Exception("Can't collide check an Entity against tracked CollidableComponents when it is not a member of a Scene"); + else if (!Scene.Tracker.CollidableComponents.ContainsKey(typeof(T))) + throw new Exception("Can't collide check an Entity against an untracked CollidableComponent type"); +#endif + + bool hit = false; + foreach (var component in Scene.Tracker.CollidableComponents[typeof(T)]) + { + if (CollideCheck(component)) + { + action(component as T); + hit = true; + } + } + return hit; + } + + public bool CollideDoByComponent(Action action, Vector2 at) where T : CollidableComponent + { +#if DEBUG + if (Scene == null) + throw new Exception("Can't collide check an Entity against tracked CollidableComponents when it is not a member of a Scene"); + else if (!Scene.Tracker.CollidableComponents.ContainsKey(typeof(T))) + throw new Exception("Can't collide check an Entity against an untracked CollidableComponent type"); +#endif + + bool hit = false; + var was = Position; + Position = at; + + foreach (var component in Scene.Tracker.CollidableComponents[typeof(T)]) + { + if (CollideCheck(component)) + { + action(component as T); + hit = true; + } + } + + Position = was; + return hit; + } + + #endregion + + #region Collide Geometry + + public bool CollidePoint(Vector2 point) + { + return Collide.CheckPoint(this, point); + } + + public bool CollidePoint(Vector2 point, Vector2 at) + { + return Collide.CheckPoint(this, point, at); + } + + public bool CollideLine(Vector2 from, Vector2 to) + { + return Collide.CheckLine(this, from, to); + } + + public bool CollideLine(Vector2 from, Vector2 to, Vector2 at) + { + return Collide.CheckLine(this, from, to, at); + } + + public bool CollideRect(Rectangle rect) + { + return Collide.CheckRect(this, rect); + } + + public bool CollideRect(Rectangle rect, Vector2 at) + { + return Collide.CheckRect(this, rect, at); + } + + #endregion + + #endregion + + #region Components Shortcuts + + /// + /// Shortcut function for adding a Component to the Entity's Components list + /// + /// The Component to add + public void Add(Component component) + { + Components.Add(component); + } + + /// + /// Shortcut function for removing an Component from the Entity's Components list + /// + /// The Component to remove + public void Remove(Component component) + { + Components.Remove(component); + } + + /// + /// Shortcut function for adding a set of Components from the Entity's Components list + /// + /// The Components to add + public void Add(params Component[] components) + { + Components.Add(components); + } + + /// + /// Shortcut function for removing a set of Components from the Entity's Components list + /// + /// The Components to remove + public void Remove(params Component[] components) + { + Components.Remove(components); + } + + public T Get() where T : Component + { + return Components.Get(); + } + + /// + /// Allows you to iterate through all Components in the Entity + /// + /// + public IEnumerator GetEnumerator() + { + return Components.GetEnumerator(); + } + + /// + /// Allows you to iterate through all Components in the Entity + /// + /// + IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + #endregion + + #region Misc Utils + + public Entity Closest(params Entity[] entities) + { + Entity closest = entities[0]; + float dist = Vector2.DistanceSquared(Position, closest.Position); + + for (int i = 1; i < entities.Length; i++) + { + float current = Vector2.DistanceSquared(Position, entities[i].Position); + if (current < dist) + { + closest = entities[i]; + dist = current; + } + } + + return closest; + } + + public Entity Closest(BitTag tag) + { + var list = Scene[tag]; + Entity closest = null; + float dist; + + if (list.Count >= 1) + { + closest = list[0]; + dist = Vector2.DistanceSquared(Position, closest.Position); + + for (int i = 1; i < list.Count; i++) + { + float current = Vector2.DistanceSquared(Position, list[i].Position); + if (current < dist) + { + closest = list[i]; + dist = current; + } + } + } + + return closest; + } + + public T SceneAs() where T : Scene + { + return Scene as T; + } + + #endregion + } +} diff --git a/MonocleEngineDemo/Monocle/Graphics/Atlas.cs b/MonocleEngineDemo/Monocle/Graphics/Atlas.cs new file mode 100644 index 0000000..2f29dc4 --- /dev/null +++ b/MonocleEngineDemo/Monocle/Graphics/Atlas.cs @@ -0,0 +1,417 @@ +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Xml; + +namespace Monocle +{ + /// + /// Represents a texture with multiple sprites placed on it. Along with it goes the placement of the independent sprites + /// inside the texture, aswell their paths that can be used when creating SpriteBanks. + /// + public class Atlas + { + public List Sources; + private Dictionary textures = new Dictionary(StringComparer.OrdinalIgnoreCase); + private Dictionary> orderedTexturesCache = new Dictionary>(); + + public enum AtlasDataFormat + { + TexturePacker_Sparrow, + CrunchXml, + CrunchBinary, + CrunchXmlOrBinary, + CrunchBinaryNoAtlas, + Packer, + PackerNoAtlas + }; + + public static Atlas FromAtlas(string path, AtlasDataFormat format) + { + var atlas = new Atlas(); + atlas.Sources = new List(); + ReadAtlasData(atlas, path, format); + return atlas; + } + + private static void ReadAtlasData(Atlas atlas, string path, AtlasDataFormat format) + { + switch (format) + { + case AtlasDataFormat.TexturePacker_Sparrow: + { + XmlDocument xml = Calc.LoadContentXML(path); + XmlElement at = xml["TextureAtlas"]; + + var texturePath = at.Attr("imagePath", ""); + var fileStream = new FileStream(Path.Combine(Path.GetDirectoryName(path), texturePath), FileMode.Open, FileAccess.Read); + var texture = Texture2D.FromStream(Engine.Instance.GraphicsDevice, fileStream); + fileStream.Close(); + + var mTexture = new MTexture(texture); + atlas.Sources.Add(texture); + + var subtextures = at.GetElementsByTagName("SubTexture"); + + foreach (XmlElement sub in subtextures) + { + var name = sub.Attr("name"); + var clipRect = sub.Rect(); + if (sub.HasAttr("frameX")) + atlas.textures[name] = new MTexture(mTexture, name, clipRect, new Vector2(-sub.AttrInt("frameX"), -sub.AttrInt("frameY")), sub.AttrInt("frameWidth"), sub.AttrInt("frameHeight")); + else + atlas.textures[name] = new MTexture(mTexture, name, clipRect); + } + } + break; + case AtlasDataFormat.CrunchXml: + { + XmlDocument xml = Calc.LoadContentXML(path); + XmlElement at = xml["atlas"]; + + foreach (XmlElement tex in at) + { + var texturePath = tex.Attr("n", ""); + string fsloc = Engine.ContentDirectory +"\\"+ Path.GetDirectoryName(path)+ texturePath + ".png"; + var fileStream = new FileStream(fsloc, FileMode.Open, FileAccess.Read); + var texture = Texture2D.FromStream(Engine.Instance.GraphicsDevice, fileStream); + fileStream.Close(); + + var mTexture = new MTexture(texture); + atlas.Sources.Add(texture); + + foreach (XmlElement sub in tex) + { + var name = sub.Attr("n"); + var clipRect = new Rectangle(sub.AttrInt("x"), sub.AttrInt("y"), sub.AttrInt("w"), sub.AttrInt("h")); + if (sub.HasAttr("fx")) + atlas.textures[name] = new MTexture(mTexture, name, clipRect, new Vector2(-sub.AttrInt("fx"), -sub.AttrInt("fy")), sub.AttrInt("fw"), sub.AttrInt("fh")); + else + atlas.textures[name] = new MTexture(mTexture, name, clipRect); + } + } + } + break; + + case AtlasDataFormat.CrunchBinary: + using (var stream = File.OpenRead(Path.Combine(Engine.ContentDirectory, path))) + { + var reader = new BinaryReader(stream); + var textures = reader.ReadInt16(); + + for (int i = 0; i < textures; i++) + { + var textureName = reader.ReadNullTerminatedString(); + var texturePath = Path.Combine(Path.GetDirectoryName(path), textureName + ".png"); + var fileStream = new FileStream(texturePath, FileMode.Open, FileAccess.Read); + var texture = Texture2D.FromStream(Engine.Instance.GraphicsDevice, fileStream); + fileStream.Close(); + + atlas.Sources.Add(texture); + + var mTexture = new MTexture(texture); + var subtextures = reader.ReadInt16(); + for (int j = 0; j < subtextures; j++) + { + var name = reader.ReadNullTerminatedString(); + var x = reader.ReadInt16(); + var y = reader.ReadInt16(); + var w = reader.ReadInt16(); + var h = reader.ReadInt16(); + var fx = reader.ReadInt16(); + var fy = reader.ReadInt16(); + var fw = reader.ReadInt16(); + var fh = reader.ReadInt16(); + + atlas.textures[name] = new MTexture(mTexture, name, new Rectangle(x, y, w, h), new Vector2(-fx, -fy), fw, fh); + } + } + } + break; + + case AtlasDataFormat.CrunchBinaryNoAtlas: + using (var stream = File.OpenRead(Path.Combine(Engine.ContentDirectory, path + ".bin"))) + { + var reader = new BinaryReader(stream); + var folders = reader.ReadInt16(); + + for (int i = 0; i < folders; i++) + { + var folderName = reader.ReadNullTerminatedString(); + var folderPath = Path.Combine(Path.GetDirectoryName(path), folderName); + + var subtextures = reader.ReadInt16(); + for (int j = 0; j < subtextures; j++) + { + var name = reader.ReadNullTerminatedString(); + var x = reader.ReadInt16(); + var y = reader.ReadInt16(); + var w = reader.ReadInt16(); + var h = reader.ReadInt16(); + var fx = reader.ReadInt16(); + var fy = reader.ReadInt16(); + var fw = reader.ReadInt16(); + var fh = reader.ReadInt16(); + + var fileStream = new FileStream(Path.Combine(folderPath, name + ".png"), FileMode.Open, FileAccess.Read); + var texture = Texture2D.FromStream(Engine.Instance.GraphicsDevice, fileStream); + fileStream.Close(); + + atlas.Sources.Add(texture); + atlas.textures[name] = new MTexture(texture, new Vector2(-fx, -fy), fw, fh); + } + } + } + break; + + case AtlasDataFormat.Packer: + + using (var stream = File.OpenRead(Path.Combine(Engine.ContentDirectory, path + ".meta"))) + { + var reader = new BinaryReader(stream); + reader.ReadInt32(); // version + reader.ReadString(); // args + reader.ReadInt32(); // hash + + var textures = reader.ReadInt16(); + for (int i = 0; i < textures; i++) + { + var textureName = reader.ReadString(); + var texturePath = Path.Combine(Path.GetDirectoryName(path), textureName + ".data"); + var fileStream = new FileStream(texturePath, FileMode.Open, FileAccess.Read); + var texture = Texture2D.FromStream(Engine.Instance.GraphicsDevice, fileStream); + fileStream.Close(); + + atlas.Sources.Add(texture); + + var mTexture = new MTexture(texture); + var subtextures = reader.ReadInt16(); + for (int j = 0; j < subtextures; j++) + { + var name = reader.ReadString().Replace('\\', '/'); + var x = reader.ReadInt16(); + var y = reader.ReadInt16(); + var w = reader.ReadInt16(); + var h = reader.ReadInt16(); + var fx = reader.ReadInt16(); + var fy = reader.ReadInt16(); + var fw = reader.ReadInt16(); + var fh = reader.ReadInt16(); + + atlas.textures[name] = new MTexture(mTexture, name, new Rectangle(x, y, w, h), new Vector2(-fx, -fy), fw, fh); + } + } + } + + break; + + case AtlasDataFormat.PackerNoAtlas: + using (var stream = File.OpenRead(Path.Combine(Engine.ContentDirectory, path + ".meta"))) + { + var reader = new BinaryReader(stream); + reader.ReadInt32(); // version + reader.ReadString(); // args + reader.ReadInt32(); // hash + + var folders = reader.ReadInt16(); + for (int i = 0; i < folders; i++) + { + var folderName = reader.ReadString(); + var folderPath = Path.Combine(Path.GetDirectoryName(path), folderName); + + var subtextures = reader.ReadInt16(); + for (int j = 0; j < subtextures; j++) + { + var name = reader.ReadString().Replace('\\', '/'); + var x = reader.ReadInt16(); + var y = reader.ReadInt16(); + var w = reader.ReadInt16(); + var h = reader.ReadInt16(); + var fx = reader.ReadInt16(); + var fy = reader.ReadInt16(); + var fw = reader.ReadInt16(); + var fh = reader.ReadInt16(); + + var fileStream = new FileStream(Path.Combine(folderPath, name + ".data"), FileMode.Open, FileAccess.Read); + var texture = Texture2D.FromStream(Engine.Instance.GraphicsDevice, fileStream); + fileStream.Close(); + + atlas.Sources.Add(texture); + atlas.textures[name] = new MTexture(texture, new Vector2(-fx, -fy), fw, fh); + } + } + } + break; + + case AtlasDataFormat.CrunchXmlOrBinary: + + if (File.Exists(Path.Combine(Engine.ContentDirectory, path + ".bin"))) + ReadAtlasData(atlas, path + ".bin", AtlasDataFormat.CrunchBinary); + else + ReadAtlasData(atlas, path + ".xml", AtlasDataFormat.CrunchXml); + + break; + + + default: + throw new NotImplementedException(); + } + } + + public static Atlas FromMultiAtlas(string rootPath, string[] dataPath, AtlasDataFormat format) + { + var atlas = new Atlas(); + atlas.Sources = new List(); + + for (int i = 0; i < dataPath.Length; i ++) + ReadAtlasData(atlas, Path.Combine(rootPath, dataPath[i]), format); + + return atlas; + } + + public static Atlas FromMultiAtlas(string rootPath, string filename, AtlasDataFormat format) + { + var atlas = new Atlas(); + atlas.Sources = new List(); + + var index = 0; + while (true) + { + var dataPath = Path.Combine(rootPath, filename + index.ToString() + ".xml"); + + if (!File.Exists(Path.Combine(Engine.ContentDirectory, dataPath))) + break; + + ReadAtlasData(atlas, dataPath, format); + index++; + } + + return atlas; + } + + public static Atlas FromDirectory(string path) + { + var atlas = new Atlas(); + atlas.Sources = new List(); + + var contentDirectory = Engine.ContentDirectory; + var contentDirectoryLength = contentDirectory.Length; + var contentPath = Path.Combine(contentDirectory, path); + var contentPathLength = contentPath.Length; + + foreach (var file in Directory.GetFiles(contentPath, "*", SearchOption.AllDirectories)) + { + var ext = Path.GetExtension(file); + if (ext != ".png" && ext != ".xnb") + continue; + + // get path and load + var fileStream = new FileStream(file.Substring(contentDirectoryLength + 1), FileMode.Open, FileAccess.Read); + var texture = Texture2D.FromStream(Engine.Instance.GraphicsDevice, fileStream); + fileStream.Close(); + + atlas.Sources.Add(texture); + + // make nice for dictionary + var filepath = file.Substring(contentPathLength + 1); + filepath = filepath.Substring(0, filepath.Length - 4); + filepath = filepath.Replace('\\', '/'); + + // load + atlas.textures.Add(filepath, new MTexture(texture)); + } + + return atlas; + } + + public MTexture this[string id] + { + get { return textures[id]; } + set { textures[id] = value; } + } + + public bool Has(string id) + { + return textures.ContainsKey(id); + } + + public MTexture GetOrDefault(string id, MTexture defaultTexture) + { + if (String.IsNullOrEmpty(id) || !Has(id)) + return defaultTexture; + return textures[id]; + } + + public List GetAtlasSubtextures(string key) + { + List list; + + if (!orderedTexturesCache.TryGetValue(key, out list)) + { + list = new List(); + + var index = 0; + while (true) + { + var texture = GetAtlasSubtextureFromAtlasAt(key, index); + if (texture != null) + list.Add(texture); + else + break; + index++; + } + + orderedTexturesCache.Add(key, list); + } + + return list; + } + + private MTexture GetAtlasSubtextureFromCacheAt(string key, int index) + { + return orderedTexturesCache[key][index]; + } + + private MTexture GetAtlasSubtextureFromAtlasAt(string key, int index) + { + if (index == 0 && textures.ContainsKey(key)) + return textures[key]; + + var indexString = index.ToString(); + var startLength = indexString.Length; + while (indexString.Length < startLength + 6) + { + MTexture result; + if (textures.TryGetValue(key + indexString, out result)) + return result; + indexString = "0" + indexString; + } + + return null; + } + + public MTexture GetAtlasSubtexturesAt(string key, int index) + { + List list; + if (orderedTexturesCache.TryGetValue(key, out list)) + return list[index]; + else + return GetAtlasSubtextureFromAtlasAt(key, index); + } + + public void Dispose() + { + foreach (var texture in Sources) + texture.Dispose(); + Sources.Clear(); + textures.Clear(); + } + + + } +} diff --git a/MonocleEngineDemo/Monocle/Graphics/MTexture.cs b/MonocleEngineDemo/Monocle/Graphics/MTexture.cs new file mode 100644 index 0000000..96c233d --- /dev/null +++ b/MonocleEngineDemo/Monocle/Graphics/MTexture.cs @@ -0,0 +1,827 @@ +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using System; +using System.Collections.Generic; +using System.IO; +using System.Xml; + +namespace Monocle +{ + /// + /// Monocle texture is a wrapper class that handles textures without any complex setups. + /// + public class MTexture + { + static public MTexture FromFile(string filename) + { + var fileStream = new FileStream(filename, FileMode.Open, FileAccess.Read); + var texture = Texture2D.FromStream(Engine.Instance.GraphicsDevice, fileStream); + fileStream.Close(); + + return new MTexture(texture); + } + + public MTexture() { } + + public MTexture(Texture2D texture) + { + Texture = texture; + AtlasPath = null; + ClipRect = new Rectangle(0, 0, Texture.Width, Texture.Height); + DrawOffset = Vector2.Zero; + Width = ClipRect.Width; + Height = ClipRect.Height; + SetUtil(); + } + + public MTexture(MTexture parent, int x, int y, int width, int height) + { + Texture = parent.Texture; + AtlasPath = null; + + ClipRect = parent.GetRelativeRect(x, y, width, height); + DrawOffset = new Vector2(-Math.Min(x - parent.DrawOffset.X, 0), -Math.Min(y - parent.DrawOffset.Y, 0)); + Width = width; + Height = height; + SetUtil(); + } + + public MTexture(MTexture parent, Rectangle clipRect) + : this(parent, clipRect.X, clipRect.Y, clipRect.Width, clipRect.Height) + { + + } + + public MTexture(MTexture parent, string atlasPath, Rectangle clipRect, Vector2 drawOffset, int width, int height) + { + Texture = parent.Texture; + AtlasPath = atlasPath; + + ClipRect = parent.GetRelativeRect(clipRect); + DrawOffset = drawOffset; + Width = width; + Height = height; + SetUtil(); + } + + public MTexture(MTexture parent, string atlasPath, Rectangle clipRect) + : this(parent, clipRect) + { + AtlasPath = atlasPath; + } + + public MTexture(Texture2D texture, Vector2 drawOffset, int frameWidth, int frameHeight) + { + Texture = texture; + ClipRect = new Rectangle(0, 0, texture.Width, texture.Height); + DrawOffset = drawOffset; + Width = frameWidth; + Height = frameHeight; + SetUtil(); + } + + public MTexture(int width, int height, Color color) + { + Texture = new Texture2D(Engine.Instance.GraphicsDevice, width, height); + var colors = new Color[width * height]; + for (int i = 0; i < width * height; i++) + colors[i] = color; + Texture.SetData(colors); + + ClipRect = new Rectangle(0, 0, width, height); + DrawOffset = Vector2.Zero; + Width = width; + Height = height; + SetUtil(); + } + + private void SetUtil() + { + Center = new Vector2(Width, Height) * 0.5f; + LeftUV = ClipRect.Left / (float)Texture.Width; + RightUV = ClipRect.Right / (float)Texture.Width; + TopUV = ClipRect.Top / (float)Texture.Height; + BottomUV = ClipRect.Bottom / (float)Texture.Height; + } + + public void Unload() + { + Texture.Dispose(); + Texture = null; + } + + public MTexture GetSubtexture(int x, int y, int width, int height, MTexture applyTo = null) + { + if (applyTo == null) + return new MTexture(this, x, y, width, height); + else + { + applyTo.Texture = Texture; + applyTo.AtlasPath = null; + + applyTo.ClipRect = GetRelativeRect(x, y, width, height); + applyTo.DrawOffset = new Vector2(-Math.Min(x - DrawOffset.X, 0), -Math.Min(y - DrawOffset.Y, 0)); + applyTo.Width = width; + applyTo.Height = height; + applyTo.SetUtil(); + + return applyTo; + } + } + + public MTexture GetSubtexture(Rectangle rect) + { + return new MTexture(this, rect); + } + + public void Dispose() + { + Texture.Dispose(); + } + + #region Properties + + public Texture2D Texture { get; private set; } + public Rectangle ClipRect { get; private set; } + public string AtlasPath { get; private set; } + public Vector2 DrawOffset { get; private set; } + public int Width { get; private set; } + public int Height { get; private set; } + public Vector2 Center { get; private set; } + public float LeftUV { get; private set; } + public float RightUV { get; private set; } + public float TopUV { get; private set; } + public float BottomUV { get; private set; } + + #endregion + + #region Helpers + + public override string ToString() + { + if (AtlasPath != null) + return AtlasPath; + else + return "MTexture [" + Texture.Width + " x " + Texture.Height + "]"; + } + + public Rectangle GetRelativeRect(Rectangle rect) + { + return GetRelativeRect(rect.X, rect.Y, rect.Width, rect.Height); + } + + public Rectangle GetRelativeRect(int x, int y, int width, int height) + { + int atX = (int)(ClipRect.X - DrawOffset.X + x); + int atY = (int)(ClipRect.Y - DrawOffset.Y + y); + + int rX = (int)MathHelper.Clamp(atX, ClipRect.Left, ClipRect.Right); + int rY = (int)MathHelper.Clamp(atY, ClipRect.Top, ClipRect.Bottom); + int rW = Math.Max(0, Math.Min(atX + width, ClipRect.Right) - rX); + int rH = Math.Max(0, Math.Min(atY + height, ClipRect.Bottom) - rY); + + return new Rectangle(rX, rY, rW, rH); + } + + + public int TotalPixels + { + get { return Width * Height; } + } + + #endregion + + #region Draw + + public void Draw(Vector2 position) + { +#if DEBUG + if (Texture.IsDisposed) + throw new Exception("Texture2D Is Disposed"); +#endif + Monocle.Draw.SpriteBatch.Draw(Texture, position, ClipRect, Color.White, 0, -DrawOffset, 1f, SpriteEffects.None, 0); + } + + public void Draw(Vector2 position, Vector2 origin) + { +#if DEBUG + if (Texture.IsDisposed) + throw new Exception("Texture2D Is Disposed"); +#endif + Monocle.Draw.SpriteBatch.Draw(Texture, position, ClipRect, Color.White, 0, origin - DrawOffset, 1f, SpriteEffects.None, 0); + } + + public void Draw(Vector2 position, Vector2 origin, Color color) + { +#if DEBUG + if (Texture.IsDisposed) + throw new Exception("Texture2D Is Disposed"); +#endif + Monocle.Draw.SpriteBatch.Draw(Texture, position, ClipRect, color, 0, origin - DrawOffset, 1f, SpriteEffects.None, 0); + } + + public void Draw(Vector2 position, Vector2 origin, Color color, float scale) + { +#if DEBUG + if (Texture.IsDisposed) + throw new Exception("Texture2D Is Disposed"); +#endif + Monocle.Draw.SpriteBatch.Draw(Texture, position, ClipRect, color, 0, origin - DrawOffset, scale, SpriteEffects.None, 0); + } + + public void Draw(Vector2 position, Vector2 origin, Color color, float scale, float rotation) + { +#if DEBUG + if (Texture.IsDisposed) + throw new Exception("Texture2D Is Disposed"); +#endif + Monocle.Draw.SpriteBatch.Draw(Texture, position, ClipRect, color, rotation, origin - DrawOffset, scale, SpriteEffects.None, 0); + } + + public void Draw(Vector2 position, Vector2 origin, Color color, float scale, float rotation, SpriteEffects flip) + { +#if DEBUG + if (Texture.IsDisposed) + throw new Exception("Texture2D Is Disposed"); +#endif + Monocle.Draw.SpriteBatch.Draw(Texture, position, ClipRect, color, rotation, origin - DrawOffset, scale, flip, 0); + } + + public void Draw(Vector2 position, Vector2 origin, Color color, Vector2 scale) + { +#if DEBUG + if (Texture.IsDisposed) + throw new Exception("Texture2D Is Disposed"); +#endif + Monocle.Draw.SpriteBatch.Draw(Texture, position, ClipRect, color, 0, origin - DrawOffset, scale, SpriteEffects.None, 0); + } + + public void Draw(Vector2 position, Vector2 origin, Color color, Vector2 scale, float rotation) + { +#if DEBUG + if (Texture.IsDisposed) + throw new Exception("Texture2D Is Disposed"); +#endif + Monocle.Draw.SpriteBatch.Draw(Texture, position, ClipRect, color, rotation, origin - DrawOffset, scale, SpriteEffects.None, 0); + } + + public void Draw(Vector2 position, Vector2 origin, Color color, Vector2 scale, float rotation, SpriteEffects flip) + { +#if DEBUG + if (Texture.IsDisposed) + throw new Exception("Texture2D Is Disposed"); +#endif + Monocle.Draw.SpriteBatch.Draw(Texture, position, ClipRect, color, rotation, origin - DrawOffset, scale, flip, 0); + } + + public void Draw(Vector2 position, Vector2 origin, Color color, Vector2 scale, float rotation, Rectangle clip) + { +#if DEBUG + if (Texture.IsDisposed) + throw new Exception("Texture2D Is Disposed"); +#endif + Monocle.Draw.SpriteBatch.Draw(Texture, position, GetRelativeRect(clip), color, rotation, origin - DrawOffset, scale, SpriteEffects.None, 0); + } + + #endregion + + #region Draw Centered + + public void DrawCentered(Vector2 position) + { +#if DEBUG + if (Texture.IsDisposed) + throw new Exception("Texture2D Is Disposed"); +#endif + Monocle.Draw.SpriteBatch.Draw(Texture, position, ClipRect, Color.White, 0, Center - DrawOffset, 1f, SpriteEffects.None, 0); + } + + public void DrawCentered(Vector2 position, Color color) + { +#if DEBUG + if (Texture.IsDisposed) + throw new Exception("Texture2D Is Disposed"); +#endif + Monocle.Draw.SpriteBatch.Draw(Texture, position, ClipRect, color, 0, Center - DrawOffset, 1f, SpriteEffects.None, 0); + } + + public void DrawCentered(Vector2 position, Color color, float scale) + { +#if DEBUG + if (Texture.IsDisposed) + throw new Exception("Texture2D Is Disposed"); +#endif + Monocle.Draw.SpriteBatch.Draw(Texture, position, ClipRect, color, 0, Center - DrawOffset, scale, SpriteEffects.None, 0); + } + + public void DrawCentered(Vector2 position, Color color, float scale, float rotation) + { +#if DEBUG + if (Texture.IsDisposed) + throw new Exception("Texture2D Is Disposed"); +#endif + Monocle.Draw.SpriteBatch.Draw(Texture, position, ClipRect, color, rotation, Center - DrawOffset, scale, SpriteEffects.None, 0); + } + + public void DrawCentered(Vector2 position, Color color, float scale, float rotation, SpriteEffects flip) + { +#if DEBUG + if (Texture.IsDisposed) + throw new Exception("Texture2D Is Disposed"); +#endif + Monocle.Draw.SpriteBatch.Draw(Texture, position, ClipRect, color, rotation, Center - DrawOffset, scale, flip, 0); + } + + public void DrawCentered(Vector2 position, Color color, Vector2 scale) + { +#if DEBUG + if (Texture.IsDisposed) + throw new Exception("Texture2D Is Disposed"); +#endif + Monocle.Draw.SpriteBatch.Draw(Texture, position, ClipRect, color, 0, Center - DrawOffset, scale, SpriteEffects.None, 0); + } + + public void DrawCentered(Vector2 position, Color color, Vector2 scale, float rotation) + { +#if DEBUG + if (Texture.IsDisposed) + throw new Exception("Texture2D Is Disposed"); +#endif + Monocle.Draw.SpriteBatch.Draw(Texture, position, ClipRect, color, rotation, Center - DrawOffset, scale, SpriteEffects.None, 0); + } + + public void DrawCentered(Vector2 position, Color color, Vector2 scale, float rotation, SpriteEffects flip) + { +#if DEBUG + if (Texture.IsDisposed) + throw new Exception("Texture2D Is Disposed"); +#endif + Monocle.Draw.SpriteBatch.Draw(Texture, position, ClipRect, color, rotation, Center - DrawOffset, scale, flip, 0); + } + + #endregion + + #region Draw Justified + + public void DrawJustified(Vector2 position, Vector2 justify) + { +#if DEBUG + if (Texture.IsDisposed) + throw new Exception("Texture2D Is Disposed"); +#endif + Monocle.Draw.SpriteBatch.Draw(Texture, position, ClipRect, Color.White, 0, new Vector2(Width * justify.X, Height * justify.Y) - DrawOffset, 1f, SpriteEffects.None, 0); + } + + public void DrawJustified(Vector2 position, Vector2 justify, Color color) + { +#if DEBUG + if (Texture.IsDisposed) + throw new Exception("Texture2D Is Disposed"); +#endif + Monocle.Draw.SpriteBatch.Draw(Texture, position, ClipRect, color, 0, new Vector2(Width * justify.X, Height * justify.Y) - DrawOffset, 1f, SpriteEffects.None, 0); + } + + public void DrawJustified(Vector2 position, Vector2 justify, Color color, float scale) + { +#if DEBUG + if (Texture.IsDisposed) + throw new Exception("Texture2D Is Disposed"); +#endif + Monocle.Draw.SpriteBatch.Draw(Texture, position, ClipRect, color, 0, new Vector2(Width * justify.X, Height * justify.Y) - DrawOffset, scale, SpriteEffects.None, 0); + } + + public void DrawJustified(Vector2 position, Vector2 justify, Color color, float scale, float rotation) + { +#if DEBUG + if (Texture.IsDisposed) + throw new Exception("Texture2D Is Disposed"); +#endif + Monocle.Draw.SpriteBatch.Draw(Texture, position, ClipRect, color, rotation, new Vector2(Width * justify.X, Height * justify.Y) - DrawOffset, scale, SpriteEffects.None, 0); + } + + public void DrawJustified(Vector2 position, Vector2 justify, Color color, float scale, float rotation, SpriteEffects flip) + { +#if DEBUG + if (Texture.IsDisposed) + throw new Exception("Texture2D Is Disposed"); +#endif + Monocle.Draw.SpriteBatch.Draw(Texture, position, ClipRect, color, rotation, new Vector2(Width * justify.X, Height * justify.Y) - DrawOffset, scale, flip, 0); + } + + public void DrawJustified(Vector2 position, Vector2 justify, Color color, Vector2 scale) + { +#if DEBUG + if (Texture.IsDisposed) + throw new Exception("Texture2D Is Disposed"); +#endif + Monocle.Draw.SpriteBatch.Draw(Texture, position, ClipRect, color, 0, new Vector2(Width * justify.X, Height * justify.Y) - DrawOffset, scale, SpriteEffects.None, 0); + } + + public void DrawJustified(Vector2 position, Vector2 justify, Color color, Vector2 scale, float rotation) + { +#if DEBUG + if (Texture.IsDisposed) + throw new Exception("Texture2D Is Disposed"); +#endif + Monocle.Draw.SpriteBatch.Draw(Texture, position, ClipRect, color, rotation, new Vector2(Width * justify.X, Height * justify.Y) - DrawOffset, scale, SpriteEffects.None, 0); + } + + public void DrawJustified(Vector2 position, Vector2 justify, Color color, Vector2 scale, float rotation, SpriteEffects flip) + { +#if DEBUG + if (Texture.IsDisposed) + throw new Exception("Texture2D Is Disposed"); +#endif + Monocle.Draw.SpriteBatch.Draw(Texture, position, ClipRect, color, rotation, new Vector2(Width * justify.X, Height * justify.Y) - DrawOffset, scale, flip, 0); + } + + #endregion + + #region Draw Outline + + public void DrawOutline(Vector2 position) + { +#if DEBUG + if (Texture.IsDisposed) + throw new Exception("Texture2D Is Disposed"); +#endif + + for (var i = -1; i <= 1; i++) + for (var j = -1; j <= 1; j++) + if (i != 0 || j != 0) + Monocle.Draw.SpriteBatch.Draw(Texture, position + new Vector2(i, j), ClipRect, Color.Black, 0, -DrawOffset, 1f, SpriteEffects.None, 0); + + Monocle.Draw.SpriteBatch.Draw(Texture, position, ClipRect, Color.White, 0, -DrawOffset, 1f, SpriteEffects.None, 0); + } + + public void DrawOutline(Vector2 position, Vector2 origin) + { +#if DEBUG + if (Texture.IsDisposed) + throw new Exception("Texture2D Is Disposed"); +#endif + + for (var i = -1; i <= 1; i++) + for (var j = -1; j <= 1; j++) + if (i != 0 || j != 0) + Monocle.Draw.SpriteBatch.Draw(Texture, position + new Vector2(i, j), ClipRect, Color.Black, 0, origin - DrawOffset, 1f, SpriteEffects.None, 0); + + Monocle.Draw.SpriteBatch.Draw(Texture, position, ClipRect, Color.White, 0, origin - DrawOffset, 1f, SpriteEffects.None, 0); + } + + public void DrawOutline(Vector2 position, Vector2 origin, Color color) + { +#if DEBUG + if (Texture.IsDisposed) + throw new Exception("Texture2D Is Disposed"); +#endif + + for (var i = -1; i <= 1; i++) + for (var j = -1; j <= 1; j++) + if (i != 0 || j != 0) + Monocle.Draw.SpriteBatch.Draw(Texture, position + new Vector2(i, j), ClipRect, Color.Black, 0, origin - DrawOffset, 1f, SpriteEffects.None, 0); + + Monocle.Draw.SpriteBatch.Draw(Texture, position, ClipRect, color, 0, origin - DrawOffset, 1f, SpriteEffects.None, 0); + } + + public void DrawOutline(Vector2 position, Vector2 origin, Color color, float scale) + { +#if DEBUG + if (Texture.IsDisposed) + throw new Exception("Texture2D Is Disposed"); +#endif + + for (var i = -1; i <= 1; i++) + for (var j = -1; j <= 1; j++) + if (i != 0 || j != 0) + Monocle.Draw.SpriteBatch.Draw(Texture, position + new Vector2(i, j), ClipRect, Color.Black, 0, origin - DrawOffset, scale, SpriteEffects.None, 0); + + Monocle.Draw.SpriteBatch.Draw(Texture, position, ClipRect, color, 0, origin - DrawOffset, scale, SpriteEffects.None, 0); + } + + public void DrawOutline(Vector2 position, Vector2 origin, Color color, float scale, float rotation) + { +#if DEBUG + if (Texture.IsDisposed) + throw new Exception("Texture2D Is Disposed"); +#endif + + for (var i = -1; i <= 1; i++) + for (var j = -1; j <= 1; j++) + if (i != 0 || j != 0) + Monocle.Draw.SpriteBatch.Draw(Texture, position + new Vector2(i, j), ClipRect, Color.Black, rotation, origin - DrawOffset, scale, SpriteEffects.None, 0); + + Monocle.Draw.SpriteBatch.Draw(Texture, position, ClipRect, color, rotation, origin - DrawOffset, scale, SpriteEffects.None, 0); + } + + public void DrawOutline(Vector2 position, Vector2 origin, Color color, float scale, float rotation, SpriteEffects flip) + { +#if DEBUG + if (Texture.IsDisposed) + throw new Exception("Texture2D Is Disposed"); +#endif + + for (var i = -1; i <= 1; i++) + for (var j = -1; j <= 1; j++) + if (i != 0 || j != 0) + Monocle.Draw.SpriteBatch.Draw(Texture, position + new Vector2(i, j), ClipRect, Color.Black, rotation, origin - DrawOffset, scale, flip, 0); + + Monocle.Draw.SpriteBatch.Draw(Texture, position, ClipRect, color, rotation, origin - DrawOffset, scale, flip, 0); + } + + public void DrawOutline(Vector2 position, Vector2 origin, Color color, Vector2 scale) + { +#if DEBUG + if (Texture.IsDisposed) + throw new Exception("Texture2D Is Disposed"); +#endif + + for (var i = -1; i <= 1; i++) + for (var j = -1; j <= 1; j++) + if (i != 0 || j != 0) + Monocle.Draw.SpriteBatch.Draw(Texture, position + new Vector2(i, j), ClipRect, Color.Black, 0, origin - DrawOffset, scale, SpriteEffects.None, 0); + + Monocle.Draw.SpriteBatch.Draw(Texture, position, ClipRect, color, 0, origin - DrawOffset, scale, SpriteEffects.None, 0); + } + + public void DrawOutline(Vector2 position, Vector2 origin, Color color, Vector2 scale, float rotation) + { +#if DEBUG + if (Texture.IsDisposed) + throw new Exception("Texture2D Is Disposed"); +#endif + + for (var i = -1; i <= 1; i++) + for (var j = -1; j <= 1; j++) + if (i != 0 || j != 0) + Monocle.Draw.SpriteBatch.Draw(Texture, position + new Vector2(i, j), ClipRect, Color.Black, rotation, origin - DrawOffset, scale, SpriteEffects.None, 0); + + Monocle.Draw.SpriteBatch.Draw(Texture, position, ClipRect, color, rotation, origin - DrawOffset, scale, SpriteEffects.None, 0); + } + + public void DrawOutline(Vector2 position, Vector2 origin, Color color, Vector2 scale, float rotation, SpriteEffects flip) + { +#if DEBUG + if (Texture.IsDisposed) + throw new Exception("Texture2D Is Disposed"); +#endif + + for (var i = -1; i <= 1; i++) + for (var j = -1; j <= 1; j++) + if (i != 0 || j != 0) + Monocle.Draw.SpriteBatch.Draw(Texture, position + new Vector2(i, j), ClipRect, Color.Black, rotation, origin - DrawOffset, scale, flip, 0); + + Monocle.Draw.SpriteBatch.Draw(Texture, position, ClipRect, color, rotation, origin - DrawOffset, scale, flip, 0); + } + + #endregion + + #region Draw Outline Centered + + public void DrawOutlineCentered(Vector2 position) + { +#if DEBUG + if (Texture.IsDisposed) + throw new Exception("Texture2D Is Disposed"); +#endif + + for (var i = -1; i <= 1; i++) + for (var j = -1; j <= 1; j++) + if (i != 0 || j != 0) + Monocle.Draw.SpriteBatch.Draw(Texture, position + new Vector2(i, j), ClipRect, Color.Black, 0, Center - DrawOffset, 1f, SpriteEffects.None, 0); + + Monocle.Draw.SpriteBatch.Draw(Texture, position, ClipRect, Color.White, 0, Center - DrawOffset, 1f, SpriteEffects.None, 0); + } + + public void DrawOutlineCentered(Vector2 position, Color color) + { +#if DEBUG + if (Texture.IsDisposed) + throw new Exception("Texture2D Is Disposed"); +#endif + + for (var i = -1; i <= 1; i++) + for (var j = -1; j <= 1; j++) + if (i != 0 || j != 0) + Monocle.Draw.SpriteBatch.Draw(Texture, position + new Vector2(i, j), ClipRect, Color.Black, 0, Center - DrawOffset, 1f, SpriteEffects.None, 0); + + Monocle.Draw.SpriteBatch.Draw(Texture, position, ClipRect, color, 0, Center - DrawOffset, 1f, SpriteEffects.None, 0); + } + + public void DrawOutlineCentered(Vector2 position, Color color, float scale) + { +#if DEBUG + if (Texture.IsDisposed) + throw new Exception("Texture2D Is Disposed"); +#endif + + for (var i = -1; i <= 1; i++) + for (var j = -1; j <= 1; j++) + if (i != 0 || j != 0) + Monocle.Draw.SpriteBatch.Draw(Texture, position + new Vector2(i, j), ClipRect, Color.Black, 0, Center - DrawOffset, scale, SpriteEffects.None, 0); + + Monocle.Draw.SpriteBatch.Draw(Texture, position, ClipRect, color, 0, Center - DrawOffset, scale, SpriteEffects.None, 0); + } + + public void DrawOutlineCentered(Vector2 position, Color color, float scale, float rotation) + { +#if DEBUG + if (Texture.IsDisposed) + throw new Exception("Texture2D Is Disposed"); +#endif + + for (var i = -1; i <= 1; i++) + for (var j = -1; j <= 1; j++) + if (i != 0 || j != 0) + Monocle.Draw.SpriteBatch.Draw(Texture, position + new Vector2(i, j), ClipRect, Color.Black, rotation, Center - DrawOffset, scale, SpriteEffects.None, 0); + + Monocle.Draw.SpriteBatch.Draw(Texture, position, ClipRect, color, rotation, Center - DrawOffset, scale, SpriteEffects.None, 0); + } + + public void DrawOutlineCentered(Vector2 position, Color color, float scale, float rotation, SpriteEffects flip) + { +#if DEBUG + if (Texture.IsDisposed) + throw new Exception("Texture2D Is Disposed"); +#endif + + for (var i = -1; i <= 1; i++) + for (var j = -1; j <= 1; j++) + if (i != 0 || j != 0) + Monocle.Draw.SpriteBatch.Draw(Texture, position + new Vector2(i, j), ClipRect, Color.Black, rotation, Center - DrawOffset, scale, flip, 0); + + Monocle.Draw.SpriteBatch.Draw(Texture, position, ClipRect, color, rotation, Center - DrawOffset, scale, flip, 0); + } + + public void DrawOutlineCentered(Vector2 position, Color color, Vector2 scale) + { +#if DEBUG + if (Texture.IsDisposed) + throw new Exception("Texture2D Is Disposed"); +#endif + + for (var i = -1; i <= 1; i++) + for (var j = -1; j <= 1; j++) + if (i != 0 || j != 0) + Monocle.Draw.SpriteBatch.Draw(Texture, position + new Vector2(i, j), ClipRect, Color.Black, 0, Center - DrawOffset, scale, SpriteEffects.None, 0); + + Monocle.Draw.SpriteBatch.Draw(Texture, position, ClipRect, color, 0, Center - DrawOffset, scale, SpriteEffects.None, 0); + } + + public void DrawOutlineCentered(Vector2 position, Color color, Vector2 scale, float rotation) + { +#if DEBUG + if (Texture.IsDisposed) + throw new Exception("Texture2D Is Disposed"); +#endif + + for (var i = -1; i <= 1; i++) + for (var j = -1; j <= 1; j++) + if (i != 0 || j != 0) + Monocle.Draw.SpriteBatch.Draw(Texture, position + new Vector2(i, j), ClipRect, Color.Black, rotation, Center - DrawOffset, scale, SpriteEffects.None, 0); + + Monocle.Draw.SpriteBatch.Draw(Texture, position, ClipRect, color, rotation, Center - DrawOffset, scale, SpriteEffects.None, 0); + } + + public void DrawOutlineCentered(Vector2 position, Color color, Vector2 scale, float rotation, SpriteEffects flip) + { +#if DEBUG + if (Texture.IsDisposed) + throw new Exception("Texture2D Is Disposed"); +#endif + + for (var i = -1; i <= 1; i++) + for (var j = -1; j <= 1; j++) + if (i != 0 || j != 0) + Monocle.Draw.SpriteBatch.Draw(Texture, position + new Vector2(i, j), ClipRect, Color.Black, rotation, Center - DrawOffset, scale, flip, 0); + + Monocle.Draw.SpriteBatch.Draw(Texture, position, ClipRect, color, rotation, Center - DrawOffset, scale, flip, 0); + } + + #endregion + + #region Draw Outline Justified + + public void DrawOutlineJustified(Vector2 position, Vector2 justify) + { +#if DEBUG + if (Texture.IsDisposed) + throw new Exception("Texture2D Is Disposed"); +#endif + + for (var i = -1; i <= 1; i++) + for (var j = -1; j <= 1; j++) + if (i != 0 || j != 0) + Monocle.Draw.SpriteBatch.Draw(Texture, position + new Vector2(i, j), ClipRect, Color.Black, 0, new Vector2(Width * justify.X, Height * justify.Y) - DrawOffset, 1f, SpriteEffects.None, 0); + + Monocle.Draw.SpriteBatch.Draw(Texture, position, ClipRect, Color.White, 0, new Vector2(Width * justify.X, Height * justify.Y) - DrawOffset, 1f, SpriteEffects.None, 0); + } + + public void DrawOutlineJustified(Vector2 position, Vector2 justify, Color color) + { +#if DEBUG + if (Texture.IsDisposed) + throw new Exception("Texture2D Is Disposed"); +#endif + + for (var i = -1; i <= 1; i++) + for (var j = -1; j <= 1; j++) + if (i != 0 || j != 0) + Monocle.Draw.SpriteBatch.Draw(Texture, position + new Vector2(i, j), ClipRect, Color.Black, 0, new Vector2(Width * justify.X, Height * justify.Y) - DrawOffset, 1f, SpriteEffects.None, 0); + + Monocle.Draw.SpriteBatch.Draw(Texture, position, ClipRect, color, 0, new Vector2(Width * justify.X, Height * justify.Y) - DrawOffset, 1f, SpriteEffects.None, 0); + } + + public void DrawOutlineJustified(Vector2 position, Vector2 justify, Color color, float scale) + { +#if DEBUG + if (Texture.IsDisposed) + throw new Exception("Texture2D Is Disposed"); +#endif + + for (var i = -1; i <= 1; i++) + for (var j = -1; j <= 1; j++) + if (i != 0 || j != 0) + Monocle.Draw.SpriteBatch.Draw(Texture, position + new Vector2(i, j), ClipRect, Color.Black, 0, new Vector2(Width * justify.X, Height * justify.Y) - DrawOffset, scale, SpriteEffects.None, 0); + + Monocle.Draw.SpriteBatch.Draw(Texture, position, ClipRect, color, 0, new Vector2(Width * justify.X, Height * justify.Y) - DrawOffset, scale, SpriteEffects.None, 0); + } + + public void DrawOutlineJustified(Vector2 position, Vector2 justify, Color color, float scale, float rotation) + { +#if DEBUG + if (Texture.IsDisposed) + throw new Exception("Texture2D Is Disposed"); +#endif + + for (var i = -1; i <= 1; i++) + for (var j = -1; j <= 1; j++) + if (i != 0 || j != 0) + Monocle.Draw.SpriteBatch.Draw(Texture, position + new Vector2(i, j), ClipRect, Color.Black, rotation, new Vector2(Width * justify.X, Height * justify.Y) - DrawOffset, scale, SpriteEffects.None, 0); + + Monocle.Draw.SpriteBatch.Draw(Texture, position, ClipRect, color, rotation, new Vector2(Width * justify.X, Height * justify.Y) - DrawOffset, scale, SpriteEffects.None, 0); + } + + public void DrawOutlineJustified(Vector2 position, Vector2 justify, Color color, float scale, float rotation, SpriteEffects flip) + { +#if DEBUG + if (Texture.IsDisposed) + throw new Exception("Texture2D Is Disposed"); +#endif + + for (var i = -1; i <= 1; i++) + for (var j = -1; j <= 1; j++) + if (i != 0 || j != 0) + Monocle.Draw.SpriteBatch.Draw(Texture, position + new Vector2(i, j), ClipRect, Color.Black, rotation, new Vector2(Width * justify.X, Height * justify.Y) - DrawOffset, scale, flip, 0); + + Monocle.Draw.SpriteBatch.Draw(Texture, position, ClipRect, color, rotation, new Vector2(Width * justify.X, Height * justify.Y) - DrawOffset, scale, flip, 0); + } + + public void DrawOutlineJustified(Vector2 position, Vector2 justify, Color color, Vector2 scale) + { +#if DEBUG + if (Texture.IsDisposed) + throw new Exception("Texture2D Is Disposed"); +#endif + + for (var i = -1; i <= 1; i++) + for (var j = -1; j <= 1; j++) + if (i != 0 || j != 0) + Monocle.Draw.SpriteBatch.Draw(Texture, position + new Vector2(i, j), ClipRect, Color.Black, 0, new Vector2(Width * justify.X, Height * justify.Y) - DrawOffset, scale, SpriteEffects.None, 0); + + Monocle.Draw.SpriteBatch.Draw(Texture, position, ClipRect, color, 0, new Vector2(Width * justify.X, Height * justify.Y) - DrawOffset, scale, SpriteEffects.None, 0); + } + + public void DrawOutlineJustified(Vector2 position, Vector2 justify, Color color, Vector2 scale, float rotation) + { +#if DEBUG + if (Texture.IsDisposed) + throw new Exception("Texture2D Is Disposed"); +#endif + + for (var i = -1; i <= 1; i++) + for (var j = -1; j <= 1; j++) + if (i != 0 || j != 0) + Monocle.Draw.SpriteBatch.Draw(Texture, position + new Vector2(i, j), ClipRect, Color.Black, rotation, new Vector2(Width * justify.X, Height * justify.Y) - DrawOffset, scale, SpriteEffects.None, 0); + + Monocle.Draw.SpriteBatch.Draw(Texture, position, ClipRect, color, rotation, new Vector2(Width * justify.X, Height * justify.Y) - DrawOffset, scale, SpriteEffects.None, 0); + } + + public void DrawOutlineJustified(Vector2 position, Vector2 justify, Color color, Vector2 scale, float rotation, SpriteEffects flip) + { +#if DEBUG + if (Texture.IsDisposed) + throw new Exception("Texture2D Is Disposed"); +#endif + + for (var i = -1; i <= 1; i++) + for (var j = -1; j <= 1; j++) + if (i != 0 || j != 0) + Monocle.Draw.SpriteBatch.Draw(Texture, position + new Vector2(i, j), ClipRect, Color.Black, rotation, new Vector2(Width * justify.X, Height * justify.Y) - DrawOffset, scale, flip, 0); + + Monocle.Draw.SpriteBatch.Draw(Texture, position, ClipRect, color, rotation, new Vector2(Width * justify.X, Height * justify.Y) - DrawOffset, scale, flip, 0); + } + + #endregion + } +} diff --git a/MonocleEngineDemo/Monocle/Graphics/SpriteBank.cs b/MonocleEngineDemo/Monocle/Graphics/SpriteBank.cs new file mode 100644 index 0000000..0aef3e7 --- /dev/null +++ b/MonocleEngineDemo/Monocle/Graphics/SpriteBank.cs @@ -0,0 +1,74 @@ +using Microsoft.Xna.Framework; +using System; +using System.Collections.Generic; +using System.Xml; + +namespace Monocle +{ + /// + /// SpriteBank is a collection of sprites. Used along with atlases to make sprite creation easier. + /// + public class SpriteBank + { + public Atlas Atlas; + public XmlDocument XML; + public Dictionary SpriteData; + + /// + /// Constructor of a SpriteBank. + /// + /// The atlas from which are the textures mapped. + /// The XML file that contains all the data of sprites and their animations. + public SpriteBank(Atlas atlas, XmlDocument xml) + { + Atlas = atlas; + XML = xml; + + SpriteData = new Dictionary(StringComparer.OrdinalIgnoreCase); + var elements = new Dictionary(); + foreach (var e in XML["Sprites"].ChildNodes) + { + if (e is XmlElement) + { + var element = e as XmlElement; + elements.Add(element.Name, element); + + if (SpriteData.ContainsKey(element.Name)) + throw new Exception("Duplicate sprite name in SpriteData: '" + element.Name + "'!"); + + var data = SpriteData[element.Name] = new SpriteData(Atlas); + if (element.HasAttr("copy")) + data.Add(elements[element.Attr("copy")], element.Attr("path")); + data.Add(element); + } + } + } + + public SpriteBank(Atlas atlas, string xmlPath) + : this(atlas, Calc.LoadContentXML(xmlPath)) + { + + } + + public bool Has(string id) + { + return SpriteData.ContainsKey(id); + } + + public Sprite Create(string id) + { + if (SpriteData.ContainsKey(id)) + return SpriteData[id].Create(); + else + throw new Exception("Missing animation name in SpriteData: '" + id + "'!"); + } + + public Sprite CreateOn(Sprite sprite, string id) + { + if (SpriteData.ContainsKey(id)) + return SpriteData[id].CreateOn(sprite); + else + throw new Exception("Missing animation name in SpriteData: '" + id + "'!"); + } + } +} diff --git a/MonocleEngineDemo/Monocle/Graphics/SpriteData.cs b/MonocleEngineDemo/Monocle/Graphics/SpriteData.cs new file mode 100644 index 0000000..8f10e37 --- /dev/null +++ b/MonocleEngineDemo/Monocle/Graphics/SpriteData.cs @@ -0,0 +1,163 @@ +using Microsoft.Xna.Framework; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Xml; + +namespace Monocle +{ + public class SpriteDataSource + { + public XmlElement XML; + public string Path; + public string OverridePath; + } + + public class SpriteData + { + public List Sources = new List(); + public Sprite Sprite; + public Atlas Atlas; + + public SpriteData(Atlas atlas) + { + Sprite = new Sprite(atlas, ""); + Atlas = atlas; + } + + public void Add(XmlElement xml, string overridePath = null) + { + var source = new SpriteDataSource(); + source.XML = xml; + source.Path = source.XML.Attr("path"); + source.OverridePath = overridePath; + + //Error Checking + { + var prefix = "Sprite '" + source.XML.Name + "': "; + + //Path + if (!source.XML.HasAttr("path") && string.IsNullOrEmpty(overridePath)) + throw new Exception(prefix + "'path' is missing!"); + + //Anims + var ids = new HashSet(); + foreach (XmlElement anim in source.XML.GetElementsByTagName("Anim")) + CheckAnimXML(anim, prefix, ids); + foreach (XmlElement loop in source.XML.GetElementsByTagName("Loop")) + CheckAnimXML(loop, prefix, ids); + + //Start + if (source.XML.HasAttr("start") && !ids.Contains(source.XML.Attr("start"))) + throw new Exception(prefix + "starting animation '" + source.XML.Attr("start") + "' is missing!"); + + //Origin + if (source.XML.HasChild("Justify") && source.XML.HasChild("Origin")) + throw new Exception(prefix + "has both Origin and Justify tags!"); + } + + //Create the Sprite + { + var normalPath = source.XML.Attr("path", ""); + var masterDelay = source.XML.AttrFloat("delay", 0); + + //Build Animations + foreach (XmlElement anim in source.XML.GetElementsByTagName("Anim")) + { + Chooser into; + if (anim.HasAttr("goto")) + into = Chooser.FromString(anim.Attr("goto")); + else + into = null; + + var id = anim.Attr("id"); + var path = anim.Attr("path", ""); + var frames = Calc.ReadCSVIntWithTricks(anim.Attr("frames", "")); + + if (!string.IsNullOrEmpty(overridePath) && HasFrames(Atlas, overridePath + path, frames)) + path = overridePath + path; + else + path = normalPath + path; + + Sprite.Add(id, path, anim.AttrFloat("delay", masterDelay), into, frames); + } + + //Build Loops + foreach (XmlElement loop in source.XML.GetElementsByTagName("Loop")) + { + var id = loop.Attr("id"); + var path = loop.Attr("path", ""); + var frames = Calc.ReadCSVIntWithTricks(loop.Attr("frames", "")); + + if (!string.IsNullOrEmpty(overridePath) && HasFrames(Atlas, overridePath + path, frames)) + path = overridePath + path; + else + path = normalPath + path; + + Sprite.AddLoop(id, path, loop.AttrFloat("delay", masterDelay), frames); + } + + //Origin + if (source.XML.HasChild("Center")) + { + Sprite.CenterOrigin(); + Sprite.Justify = new Vector2(.5f, .5f); + } + else if (source.XML.HasChild("Justify")) + { + Sprite.JustifyOrigin(source.XML.ChildPosition("Justify")); + Sprite.Justify = source.XML.ChildPosition("Justify"); + } + else if (source.XML.HasChild("Origin")) + Sprite.Origin = source.XML.ChildPosition("Origin"); + + //Position + if (source.XML.HasChild("Position")) + Sprite.Position = source.XML.ChildPosition("Position"); + + //Start Animation + if (source.XML.HasAttr("start")) + Sprite.Play(source.XML.Attr("start")); + } + + Sources.Add(source); + } + + private bool HasFrames(Atlas atlas, string path, int[] frames = null) + { + if (frames == null || frames.Length <= 0) + return atlas.GetAtlasSubtexturesAt(path, 0) != null; + else + { + for (int i = 0; i < frames.Length; i++) + if (atlas.GetAtlasSubtexturesAt(path, frames[i]) == null) + return false; + + return true; + } + } + + private void CheckAnimXML(XmlElement xml, string prefix, HashSet ids) + { + if (!xml.HasAttr("id")) + throw new Exception(prefix + "'id' is missing on " + xml.Name + "!"); + + if (ids.Contains(xml.Attr("id"))) + throw new Exception(prefix + "multiple animations with id '" + xml.Attr("id") + "'!"); + + ids.Add(xml.Attr("id")); + } + + public Sprite Create() + { + return Sprite.CreateClone(); + } + + public Sprite CreateOn(Sprite sprite) + { + return Sprite.CloneInto(sprite); + } + } +} diff --git a/MonocleEngineDemo/Monocle/Graphics/Tileset.cs b/MonocleEngineDemo/Monocle/Graphics/Tileset.cs new file mode 100644 index 0000000..a8930b2 --- /dev/null +++ b/MonocleEngineDemo/Monocle/Graphics/Tileset.cs @@ -0,0 +1,60 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Xml; + +namespace Monocle +{ + public class Tileset + { + private MTexture[,] tiles; + + public Tileset(MTexture texture, int tileWidth, int tileHeight) + { + Texture = texture; + TileWidth = tileWidth; + TileHeight = TileHeight; + + tiles = new MTexture[Texture.Width / tileWidth, Texture.Height / tileHeight]; + for (int x = 0; x < Texture.Width / tileWidth; x++) + for (int y = 0; y < Texture.Height / tileHeight; y++) + tiles[x, y] = new MTexture(Texture, x * tileWidth, y * tileHeight, tileWidth, tileHeight); + } + + public MTexture Texture + { + get; private set; + } + + public int TileWidth + { + get; private set; + } + + public int TileHeight + { + get; private set; + } + + public MTexture this[int x, int y] + { + get + { + return tiles[x, y]; + } + } + + public MTexture this[int index] + { + get + { + if (index < 0) + return null; + else + return tiles[index % tiles.GetLength(0), index / tiles.GetLength(0)]; + } + } + } +} diff --git a/MonocleEngineDemo/Monocle/Input/MInput.cs b/MonocleEngineDemo/Monocle/Input/MInput.cs new file mode 100644 index 0000000..04288b2 --- /dev/null +++ b/MonocleEngineDemo/Monocle/Input/MInput.cs @@ -0,0 +1,872 @@ +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Input; +using System; +using System.Collections.Generic; + +namespace Monocle +{ + public static class MInput + { + public static KeyboardData Keyboard { get; private set; } + public static MouseData Mouse { get; private set; } + public static GamePadData[] GamePads { get; private set; } + + internal static List VirtualInputs; + + public static bool Active = true; + public static bool Disabled = false; + + internal static void Initialize() + { + //Init devices + Keyboard = new KeyboardData(); + Mouse = new MouseData(); + GamePads = new GamePadData[4]; + for (int i = 0; i < 4; i++) + GamePads[i] = new GamePadData((PlayerIndex)i); + VirtualInputs = new List(); + } + + internal static void Shutdown() + { + foreach (var gamepad in GamePads) + gamepad.StopRumble(); + } + + internal static void Update() + { + if (Engine.Instance.IsActive && Active) + { + if (Engine.Commands.Open) + { + Keyboard.UpdateNull(); + Mouse.UpdateNull(); + } + else + { + Keyboard.Update(); + Mouse.Update(); + } + + for (int i = 0; i < 4; i++) + GamePads[i].Update(); + } + else + { + Keyboard.UpdateNull(); + Mouse.UpdateNull(); + for (int i = 0; i < 4; i++) + GamePads[i].UpdateNull(); + } + + UpdateVirtualInputs(); + } + + public static void UpdateNull() + { + Keyboard.UpdateNull(); + Mouse.UpdateNull(); + for (int i = 0; i < 4; i++) + GamePads[i].UpdateNull(); + + UpdateVirtualInputs(); + } + + private static void UpdateVirtualInputs() + { + foreach (var virtualInput in VirtualInputs) + virtualInput.Update(); + } + + #region Keyboard + + public class KeyboardData + { + public KeyboardState PreviousState; + public KeyboardState CurrentState; + + internal KeyboardData() + { + + } + + internal void Update() + { + PreviousState = CurrentState; + CurrentState = Microsoft.Xna.Framework.Input.Keyboard.GetState(); + } + + internal void UpdateNull() + { + PreviousState = CurrentState; + CurrentState = new KeyboardState(); + } + + #region Basic Checks + + public bool Check(Keys key) + { + if (Disabled) + return false; + + return CurrentState.IsKeyDown(key); + } + + public bool Pressed(Keys key) + { + if (Disabled) + return false; + + return CurrentState.IsKeyDown(key) && !PreviousState.IsKeyDown(key); + } + + public bool Released(Keys key) + { + if (Disabled) + return false; + + return !CurrentState.IsKeyDown(key) && PreviousState.IsKeyDown(key); + } + + #endregion + + #region Convenience Checks + + public bool Check(Keys keyA, Keys keyB) + { + return Check(keyA) || Check(keyB); + } + + public bool Pressed(Keys keyA, Keys keyB) + { + return Pressed(keyA) || Pressed(keyB); + } + + public bool Released(Keys keyA, Keys keyB) + { + return Released(keyA) || Released(keyB); + } + + public bool Check(Keys keyA, Keys keyB, Keys keyC) + { + return Check(keyA) || Check(keyB) || Check(keyC); + } + + public bool Pressed(Keys keyA, Keys keyB, Keys keyC) + { + return Pressed(keyA) || Pressed(keyB) || Pressed(keyC); + } + + public bool Released(Keys keyA, Keys keyB, Keys keyC) + { + return Released(keyA) || Released(keyB) || Released(keyC); + } + + #endregion + + #region Axis + + public int AxisCheck(Keys negative, Keys positive) + { + if (Check(negative)) + { + if (Check(positive)) + return 0; + else + return -1; + } + else if (Check(positive)) + return 1; + else + return 0; + } + + public int AxisCheck(Keys negative, Keys positive, int both) + { + if (Check(negative)) + { + if (Check(positive)) + return both; + else + return -1; + } + else if (Check(positive)) + return 1; + else + return 0; + } + + #endregion + } + + #endregion + + #region Mouse + + public class MouseData + { + public MouseState PreviousState; + public MouseState CurrentState; + + internal MouseData() + { + PreviousState = new MouseState(); + CurrentState = new MouseState(); + } + + internal void Update() + { + PreviousState = CurrentState; + CurrentState = Microsoft.Xna.Framework.Input.Mouse.GetState(); + } + + internal void UpdateNull() + { + PreviousState = CurrentState; + CurrentState = new MouseState(); + } + + #region Buttons + + public bool CheckLeftButton + { + get { return CurrentState.LeftButton == ButtonState.Pressed; } + } + + public bool CheckRightButton + { + get { return CurrentState.RightButton == ButtonState.Pressed; } + } + + public bool CheckMiddleButton + { + get { return CurrentState.MiddleButton == ButtonState.Pressed; } + } + + public bool PressedLeftButton + { + get { return CurrentState.LeftButton == ButtonState.Pressed && PreviousState.LeftButton == ButtonState.Released; } + } + + public bool PressedRightButton + { + get { return CurrentState.RightButton == ButtonState.Pressed && PreviousState.RightButton == ButtonState.Released; } + } + + public bool PressedMiddleButton + { + get { return CurrentState.MiddleButton == ButtonState.Pressed && PreviousState.MiddleButton == ButtonState.Released; } + } + + public bool ReleasedLeftButton + { + get { return CurrentState.LeftButton == ButtonState.Released && PreviousState.LeftButton == ButtonState.Pressed; } + } + + public bool ReleasedRightButton + { + get { return CurrentState.RightButton == ButtonState.Released && PreviousState.RightButton == ButtonState.Pressed; } + } + + public bool ReleasedMiddleButton + { + get { return CurrentState.MiddleButton == ButtonState.Released && PreviousState.MiddleButton == ButtonState.Pressed; } + } + + #endregion + + #region Wheel + + public int Wheel + { + get { return CurrentState.ScrollWheelValue; } + } + + public int WheelDelta + { + get { return CurrentState.ScrollWheelValue - PreviousState.ScrollWheelValue; } + } + + #endregion + + #region Position + + public bool WasMoved + { + get + { + return CurrentState.X != PreviousState.X + || CurrentState.Y != PreviousState.Y; + } + } + + public float X + { + get { return Position.X; } + set { Position = new Vector2(value, Position.Y); } + } + + public float Y + { + get { return Position.Y; } + set { Position = new Vector2(Position.X, value); } + } + + public Vector2 Position + { + get + { + return Vector2.Transform(new Vector2(CurrentState.X, CurrentState.Y), Matrix.Invert(Engine.ScreenMatrix)); + } + + set + { + var vector = Vector2.Transform(value, Engine.ScreenMatrix); + Microsoft.Xna.Framework.Input.Mouse.SetPosition((int)Math.Round(vector.X), (int)Math.Round(vector.Y)); + } + } + + #endregion + } + + #endregion + + #region GamePads + + public class GamePadData + { + public PlayerIndex PlayerIndex { get; private set; } + public GamePadState PreviousState; + public GamePadState CurrentState; + public bool Attached; + + private float rumbleStrength; + private float rumbleTime; + + internal GamePadData(PlayerIndex playerIndex) + { + PlayerIndex = playerIndex; + } + + public void Update() + { + PreviousState = CurrentState; + CurrentState = Microsoft.Xna.Framework.Input.GamePad.GetState(PlayerIndex); + Attached = CurrentState.IsConnected; + + if (rumbleTime > 0) + { + rumbleTime -= Engine.DeltaTime; + if (rumbleTime <= 0) + GamePad.SetVibration(PlayerIndex, 0, 0); + } + } + + public void UpdateNull() + { + PreviousState = CurrentState; + CurrentState = new GamePadState(); + Attached = Microsoft.Xna.Framework.Input.GamePad.GetState(PlayerIndex).IsConnected; + + if (rumbleTime > 0) + rumbleTime -= Engine.DeltaTime; + + GamePad.SetVibration(PlayerIndex, 0, 0); + } + + public void Rumble(float strength, float time) + { + if (rumbleTime <= 0 || strength > rumbleStrength || (strength == rumbleStrength && time > rumbleTime)) + { + GamePad.SetVibration(PlayerIndex, strength, strength); + rumbleStrength = strength; + rumbleTime = time; + } + } + + public void StopRumble() + { + GamePad.SetVibration(PlayerIndex, 0, 0); + rumbleTime = 0; + } + + #region Buttons + + public bool Check(Buttons button) + { + if (Disabled) + return false; + + return CurrentState.IsButtonDown(button); + } + + public bool Pressed(Buttons button) + { + if (Disabled) + return false; + + return CurrentState.IsButtonDown(button) && PreviousState.IsButtonUp(button); + } + + public bool Released(Buttons button) + { + if (Disabled) + return false; + + return CurrentState.IsButtonUp(button) && PreviousState.IsButtonDown(button); + } + + public bool Check(Buttons buttonA, Buttons buttonB) + { + return Check(buttonA) || Check(buttonB); + } + + public bool Pressed(Buttons buttonA, Buttons buttonB) + { + return Pressed(buttonA) || Pressed(buttonB); + } + + public bool Released(Buttons buttonA, Buttons buttonB) + { + return Released(buttonA) || Released(buttonB); + } + + public bool Check(Buttons buttonA, Buttons buttonB, Buttons buttonC) + { + return Check(buttonA) || Check(buttonB) || Check(buttonC); + } + + public bool Pressed(Buttons buttonA, Buttons buttonB, Buttons buttonC) + { + return Pressed(buttonA) || Pressed(buttonB) || Check(buttonC); + } + + public bool Released(Buttons buttonA, Buttons buttonB, Buttons buttonC) + { + return Released(buttonA) || Released(buttonB) || Check(buttonC); + } + + #endregion + + #region Sticks + + public Vector2 GetLeftStick() + { + Vector2 ret = CurrentState.ThumbSticks.Left; + ret.Y = -ret.Y; + return ret; + } + + public Vector2 GetLeftStick(float deadzone) + { + Vector2 ret = CurrentState.ThumbSticks.Left; + if (ret.LengthSquared() < deadzone * deadzone) + ret = Vector2.Zero; + else + ret.Y = -ret.Y; + return ret; + } + + public Vector2 GetRightStick() + { + Vector2 ret = CurrentState.ThumbSticks.Right; + ret.Y = -ret.Y; + return ret; + } + + public Vector2 GetRightStick(float deadzone) + { + Vector2 ret = CurrentState.ThumbSticks.Right; + if (ret.LengthSquared() < deadzone * deadzone) + ret = Vector2.Zero; + else + ret.Y = -ret.Y; + return ret; + } + + #region Left Stick Directions + + public bool LeftStickLeftCheck(float deadzone) + { + return CurrentState.ThumbSticks.Left.X <= -deadzone; + } + + public bool LeftStickLeftPressed(float deadzone) + { + return CurrentState.ThumbSticks.Left.X <= -deadzone && PreviousState.ThumbSticks.Left.X > -deadzone; + } + + public bool LeftStickLeftReleased(float deadzone) + { + return CurrentState.ThumbSticks.Left.X > -deadzone && PreviousState.ThumbSticks.Left.X <= -deadzone; + } + + public bool LeftStickRightCheck(float deadzone) + { + return CurrentState.ThumbSticks.Left.X >= deadzone; + } + + public bool LeftStickRightPressed(float deadzone) + { + return CurrentState.ThumbSticks.Left.X >= deadzone && PreviousState.ThumbSticks.Left.X < deadzone; + } + + public bool LeftStickRightReleased(float deadzone) + { + return CurrentState.ThumbSticks.Left.X < deadzone && PreviousState.ThumbSticks.Left.X >= deadzone; + } + + public bool LeftStickDownCheck(float deadzone) + { + return CurrentState.ThumbSticks.Left.Y <= -deadzone; + } + + public bool LeftStickDownPressed(float deadzone) + { + return CurrentState.ThumbSticks.Left.Y <= -deadzone && PreviousState.ThumbSticks.Left.Y > -deadzone; + } + + public bool LeftStickDownReleased(float deadzone) + { + return CurrentState.ThumbSticks.Left.Y > -deadzone && PreviousState.ThumbSticks.Left.Y <= -deadzone; + } + + public bool LeftStickUpCheck(float deadzone) + { + return CurrentState.ThumbSticks.Left.Y >= deadzone; + } + + public bool LeftStickUpPressed(float deadzone) + { + return CurrentState.ThumbSticks.Left.Y >= deadzone && PreviousState.ThumbSticks.Left.Y < deadzone; + } + + public bool LeftStickUpReleased(float deadzone) + { + return CurrentState.ThumbSticks.Left.Y < deadzone && PreviousState.ThumbSticks.Left.Y >= deadzone; + } + + public float LeftStickHorizontal(float deadzone) + { + float h = CurrentState.ThumbSticks.Left.X; + if (Math.Abs(h) < deadzone) + return 0; + else + return h; + } + + public float LeftStickVertical(float deadzone) + { + float v = CurrentState.ThumbSticks.Left.Y; + if (Math.Abs(v) < deadzone) + return 0; + else + return -v; + } + + #endregion + + #region Right Stick Directions + + public bool RightStickLeftCheck(float deadzone) + { + return CurrentState.ThumbSticks.Right.X <= -deadzone; + } + + public bool RightStickLeftPressed(float deadzone) + { + return CurrentState.ThumbSticks.Right.X <= -deadzone && PreviousState.ThumbSticks.Right.X > -deadzone; + } + + public bool RightStickLeftReleased(float deadzone) + { + return CurrentState.ThumbSticks.Right.X > -deadzone && PreviousState.ThumbSticks.Right.X <= -deadzone; + } + + public bool RightStickRightCheck(float deadzone) + { + return CurrentState.ThumbSticks.Right.X >= deadzone; + } + + public bool RightStickRightPressed(float deadzone) + { + return CurrentState.ThumbSticks.Right.X >= deadzone && PreviousState.ThumbSticks.Right.X < deadzone; + } + + public bool RightStickRightReleased(float deadzone) + { + return CurrentState.ThumbSticks.Right.X < deadzone && PreviousState.ThumbSticks.Right.X >= deadzone; + } + + public bool RightStickUpCheck(float deadzone) + { + return CurrentState.ThumbSticks.Right.Y <= -deadzone; + } + + public bool RightStickUpPressed(float deadzone) + { + return CurrentState.ThumbSticks.Right.Y <= -deadzone && PreviousState.ThumbSticks.Right.Y > -deadzone; + } + + public bool RightStickUpReleased(float deadzone) + { + return CurrentState.ThumbSticks.Right.Y > -deadzone && PreviousState.ThumbSticks.Right.Y <= -deadzone; + } + + public bool RightStickDownCheck(float deadzone) + { + return CurrentState.ThumbSticks.Right.Y >= deadzone; + } + + public bool RightStickDownPressed(float deadzone) + { + return CurrentState.ThumbSticks.Right.Y >= deadzone && PreviousState.ThumbSticks.Right.Y < deadzone; + } + + public bool RightStickDownReleased(float deadzone) + { + return CurrentState.ThumbSticks.Right.Y < deadzone && PreviousState.ThumbSticks.Right.Y >= deadzone; + } + + public float RightStickHorizontal(float deadzone) + { + float h = CurrentState.ThumbSticks.Right.X; + if (Math.Abs(h) < deadzone) + return 0; + else + return h; + } + + public float RightStickVertical(float deadzone) + { + float v = CurrentState.ThumbSticks.Right.Y; + if (Math.Abs(v) < deadzone) + return 0; + else + return -v; + } + + #endregion + + #endregion + + #region DPad + + public int DPadHorizontal + { + get + { + return CurrentState.DPad.Right == ButtonState.Pressed ? 1 : (CurrentState.DPad.Left == ButtonState.Pressed ? -1 : 0); + } + } + + public int DPadVertical + { + get + { + return CurrentState.DPad.Down == ButtonState.Pressed ? 1 : (CurrentState.DPad.Up == ButtonState.Pressed ? -1 : 0); + } + } + + public Vector2 DPad + { + get + { + return new Vector2(DPadHorizontal, DPadVertical); + } + } + + public bool DPadLeftCheck + { + get + { + return CurrentState.DPad.Left == ButtonState.Pressed; + } + } + + public bool DPadLeftPressed + { + get + { + return CurrentState.DPad.Left == ButtonState.Pressed && PreviousState.DPad.Left == ButtonState.Released; + } + } + + public bool DPadLeftReleased + { + get + { + return CurrentState.DPad.Left == ButtonState.Released && PreviousState.DPad.Left == ButtonState.Pressed; + } + } + + public bool DPadRightCheck + { + get + { + return CurrentState.DPad.Right == ButtonState.Pressed; + } + } + + public bool DPadRightPressed + { + get + { + return CurrentState.DPad.Right == ButtonState.Pressed && PreviousState.DPad.Right == ButtonState.Released; + } + } + + public bool DPadRightReleased + { + get + { + return CurrentState.DPad.Right == ButtonState.Released && PreviousState.DPad.Right == ButtonState.Pressed; + } + } + + public bool DPadUpCheck + { + get + { + return CurrentState.DPad.Up == ButtonState.Pressed; + } + } + + public bool DPadUpPressed + { + get + { + return CurrentState.DPad.Up == ButtonState.Pressed && PreviousState.DPad.Up == ButtonState.Released; + } + } + + public bool DPadUpReleased + { + get + { + return CurrentState.DPad.Up == ButtonState.Released && PreviousState.DPad.Up == ButtonState.Pressed; + } + } + + public bool DPadDownCheck + { + get + { + return CurrentState.DPad.Down == ButtonState.Pressed; + } + } + + public bool DPadDownPressed + { + get + { + return CurrentState.DPad.Down == ButtonState.Pressed && PreviousState.DPad.Down == ButtonState.Released; + } + } + + public bool DPadDownReleased + { + get + { + return CurrentState.DPad.Down == ButtonState.Released && PreviousState.DPad.Down == ButtonState.Pressed; + } + } + + #endregion + + #region Triggers + + public bool LeftTriggerCheck(float threshold) + { + if (Disabled) + return false; + + return CurrentState.Triggers.Left >= threshold; + } + + public bool LeftTriggerPressed(float threshold) + { + if (Disabled) + return false; + + return CurrentState.Triggers.Left >= threshold && PreviousState.Triggers.Left < threshold; + } + + public bool LeftTriggerReleased(float threshold) + { + if (Disabled) + return false; + + return CurrentState.Triggers.Left < threshold && PreviousState.Triggers.Left >= threshold; + } + + public bool RightTriggerCheck(float threshold) + { + if (Disabled) + return false; + + return CurrentState.Triggers.Right >= threshold; + } + + public bool RightTriggerPressed(float threshold) + { + if (Disabled) + return false; + + return CurrentState.Triggers.Right >= threshold && PreviousState.Triggers.Right < threshold; + } + + public bool RightTriggerReleased(float threshold) + { + if (Disabled) + return false; + + return CurrentState.Triggers.Right < threshold && PreviousState.Triggers.Right >= threshold; + } + + #endregion + } + + #endregion + + #region Helpers + + public static void RumbleFirst(float strength, float time) + { + GamePads[0].Rumble(strength, time); + } + + public static int Axis(bool negative, bool positive, int bothValue) + { + if (negative) + { + if (positive) + return bothValue; + else + return -1; + } + else if (positive) + return 1; + else + return 0; + } + + public static int Axis(float axisValue, float deadzone) + { + if (Math.Abs(axisValue) >= deadzone) + return Math.Sign(axisValue); + else + return 0; + } + + public static int Axis(bool negative, bool positive, int bothValue, float axisValue, float deadzone) + { + int ret = Axis(axisValue, deadzone); + if (ret == 0) + ret = Axis(negative, positive, bothValue); + return ret; + } + + #endregion + } +} diff --git a/MonocleEngineDemo/Monocle/Input/VirtualAxis.cs b/MonocleEngineDemo/Monocle/Input/VirtualAxis.cs new file mode 100644 index 0000000..482a72f --- /dev/null +++ b/MonocleEngineDemo/Monocle/Input/VirtualAxis.cs @@ -0,0 +1,249 @@ +using Microsoft.Xna.Framework.Input; +using System.Collections.Generic; +using System; + +namespace Monocle +{ + /// + /// A virtual input represented as a float between -1 and 1 + /// + public class VirtualAxis : VirtualInput + { + public List Nodes; + + public float Value { get; private set; } + public float PreviousValue { get; private set; } + + public VirtualAxis() + : base() + { + Nodes = new List(); + } + + public VirtualAxis(params Node[] nodes) + : base() + { + Nodes = new List(nodes); + } + + public override void Update() + { + foreach (var node in Nodes) + node.Update(); + + PreviousValue = Value; + Value = 0; + foreach (var node in Nodes) + { + float value = node.Value; + if (value != 0) + { + Value = value; + break; + } + } + } + + public static implicit operator float(VirtualAxis axis) + { + return axis.Value; + } + + public abstract class Node : VirtualInputNode + { + public abstract float Value { get; } + } + + public class PadLeftStickX : Node + { + public int GamepadIndex; + public float Deadzone; + + public PadLeftStickX(int gamepadIndex, float deadzone) + { + GamepadIndex = gamepadIndex; + Deadzone = deadzone; + } + + public override float Value + { + get + { + return Calc.SignThreshold(MInput.GamePads[GamepadIndex].GetLeftStick().X, Deadzone); + } + } + } + + public class PadLeftStickY : Node + { + public int GamepadIndex; + public float Deadzone; + + public PadLeftStickY(int gamepadIndex, float deadzone) + { + GamepadIndex = gamepadIndex; + Deadzone = deadzone; + } + + public override float Value + { + get + { + return Calc.SignThreshold(MInput.GamePads[GamepadIndex].GetLeftStick().Y, Deadzone); + } + } + } + + public class PadRightStickX : Node + { + public int GamepadIndex; + public float Deadzone; + + public PadRightStickX(int gamepadIndex, float deadzone) + { + GamepadIndex = gamepadIndex; + Deadzone = deadzone; + } + + public override float Value + { + get + { + return Calc.SignThreshold(MInput.GamePads[GamepadIndex].GetRightStick().X, Deadzone); + } + } + } + + public class PadRightStickY : Node + { + public int GamepadIndex; + public float Deadzone; + + public PadRightStickY(int gamepadIndex, float deadzone) + { + GamepadIndex = gamepadIndex; + Deadzone = deadzone; + } + + public override float Value + { + get + { + return Calc.SignThreshold(MInput.GamePads[GamepadIndex].GetRightStick().Y, Deadzone); + } + } + } + + public class PadDpadLeftRight : Node + { + public int GamepadIndex; + + public PadDpadLeftRight(int gamepadIndex) + { + GamepadIndex = gamepadIndex; + } + + public override float Value + { + get + { + if (MInput.GamePads[GamepadIndex].DPadRightCheck) + return 1f; + else if (MInput.GamePads[GamepadIndex].DPadLeftCheck) + return -1f; + else + return 0; + } + } + } + + public class PadDpadUpDown : Node + { + public int GamepadIndex; + + public PadDpadUpDown(int gamepadIndex) + { + GamepadIndex = gamepadIndex; + } + + public override float Value + { + get + { + if (MInput.GamePads[GamepadIndex].DPadDownCheck) + return 1f; + else if (MInput.GamePads[GamepadIndex].DPadUpCheck) + return -1f; + else + return 0; + } + } + } + + public class KeyboardKeys : Node + { + public OverlapBehaviors OverlapBehavior; + public Keys Positive; + public Keys Negative; + + private float value; + private bool turned; + + public KeyboardKeys(OverlapBehaviors overlapBehavior, Keys negative, Keys positive) + { + OverlapBehavior = overlapBehavior; + Negative = negative; + Positive = positive; + } + + public override void Update() + { + if (MInput.Keyboard.Check(Positive)) + { + if (MInput.Keyboard.Check(Negative)) + { + switch (OverlapBehavior) + { + default: + case OverlapBehaviors.CancelOut: + value = 0; + break; + + case OverlapBehaviors.TakeNewer: + if (!turned) + { + value *= -1; + turned = true; + } + break; + + case OverlapBehaviors.TakeOlder: + //value stays the same + break; + } + } + else + { + turned = false; + value = 1; + } + } + else if (MInput.Keyboard.Check(Negative)) + { + turned = false; + value = -1; + } + else + { + turned = false; + value = 0; + } + } + + public override float Value + { + get { return value; } + } + } + } +} diff --git a/MonocleEngineDemo/Monocle/Input/VirtualButton.cs b/MonocleEngineDemo/Monocle/Input/VirtualButton.cs new file mode 100644 index 0000000..1a42abb --- /dev/null +++ b/MonocleEngineDemo/Monocle/Input/VirtualButton.cs @@ -0,0 +1,902 @@ +using Microsoft.Xna.Framework.Input; +using System.Collections.Generic; + +namespace Monocle +{ + /// + /// A virtual input that is represented as a boolean. As well as simply checking the current button state, you can ask whether it was just pressed or released this frame. You can also keep the button press stored in a buffer for a limited time, or until it is consumed by calling ConsumeBuffer() + /// + public class VirtualButton : VirtualInput + { + public List Nodes; + public float BufferTime; + public bool Repeating { get; private set; } + + private float firstRepeatTime; + private float multiRepeatTime; + private float bufferCounter; + private float repeatCounter; + private bool canRepeat; + private bool consumed; + + public VirtualButton(float bufferTime) + : base() + { + Nodes = new List(); + BufferTime = bufferTime; + } + + public VirtualButton() + : this(0) + { + + } + + public VirtualButton(float bufferTime, params Node[] nodes) + : base() + { + Nodes = new List(nodes); + BufferTime = bufferTime; + } + + public VirtualButton(params Node[] nodes) + : this(0, nodes) + { + + } + + public void SetRepeat(float repeatTime) + { + SetRepeat(repeatTime, repeatTime); + } + + public void SetRepeat(float firstRepeatTime, float multiRepeatTime) + { + this.firstRepeatTime = firstRepeatTime; + this.multiRepeatTime = multiRepeatTime; + canRepeat = (this.firstRepeatTime > 0); + if (!canRepeat) + Repeating = false; + } + + public override void Update() + { + consumed = false; + bufferCounter -= Engine.DeltaTime; + + bool check = false; + foreach (var node in Nodes) + { + node.Update(); + if (node.Pressed) + { + bufferCounter = BufferTime; + check = true; + } + else if (node.Check) + check = true; + } + + if (!check) + { + Repeating = false; + repeatCounter = 0; + bufferCounter = 0; + } + else if (canRepeat) + { + Repeating = false; + if (repeatCounter == 0) + repeatCounter = firstRepeatTime; + else + { + repeatCounter -= Engine.DeltaTime; + if (repeatCounter <= 0) + { + Repeating = true; + repeatCounter = multiRepeatTime; + } + } + } + } + + public bool Check + { + get + { + if (MInput.Disabled) + return false; + + foreach (var node in Nodes) + if (node.Check) + return true; + return false; + } + } + + public bool Pressed + { + get + { + if (MInput.Disabled) + return false; + + if (consumed) + return false; + + if (bufferCounter > 0 || Repeating) + return true; + + foreach (var node in Nodes) + if (node.Pressed) + return true; + return false; + } + } + + public bool Released + { + get + { + if (MInput.Disabled) + return false; + + foreach (var node in Nodes) + if (node.Released) + return true; + return false; + } + } + + /// + /// Ends the Press buffer for this button + /// + public void ConsumeBuffer() + { + bufferCounter = 0; + } + + /// + /// This button will not register a Press for the rest of the current frame, but otherwise continues to function normally. If the player continues to hold the button, next frame will not count as a Press. Also ends the Press buffer for this button + /// + public void ConsumePress() + { + bufferCounter = 0; + consumed = true; + } + + public static implicit operator bool(VirtualButton button) + { + return button.Check; + } + + public abstract class Node : VirtualInputNode + { + public abstract bool Check { get; } + public abstract bool Pressed { get; } + public abstract bool Released { get; } + } + + public class KeyboardKey : Node + { + public Keys Key; + + public KeyboardKey(Keys key) + { + Key = key; + } + + public override bool Check + { + get { return MInput.Keyboard.Check(Key); } + } + + public override bool Pressed + { + get { return MInput.Keyboard.Pressed(Key); } + } + + public override bool Released + { + get { return MInput.Keyboard.Released(Key); } + } + } + + public class PadButton : Node + { + public int GamepadIndex; + public Buttons Button; + + public PadButton(int gamepadIndex, Buttons button) + { + GamepadIndex = gamepadIndex; + Button = button; + } + + public override bool Check + { + get { return MInput.GamePads[GamepadIndex].Check(Button); } + } + + public override bool Pressed + { + get { return MInput.GamePads[GamepadIndex].Pressed(Button); } + } + + public override bool Released + { + get { return MInput.GamePads[GamepadIndex].Released(Button); } + } + } + + #region Pad Left Stick + + public class PadLeftStickRight : Node + { + public int GamepadIndex; + public float Deadzone; + + public PadLeftStickRight(int gamepadindex, float deadzone) + { + GamepadIndex = gamepadindex; + Deadzone = deadzone; + } + + public override bool Check + { + get { return MInput.GamePads[GamepadIndex].LeftStickRightCheck(Deadzone); } + } + + public override bool Pressed + { + get { return MInput.GamePads[GamepadIndex].LeftStickRightPressed(Deadzone); } + } + + public override bool Released + { + get { return MInput.GamePads[GamepadIndex].LeftStickRightReleased(Deadzone); } + } + } + + public class PadLeftStickLeft : Node + { + public int GamepadIndex; + public float Deadzone; + + public PadLeftStickLeft(int gamepadindex, float deadzone) + { + GamepadIndex = gamepadindex; + Deadzone = deadzone; + } + + public override bool Check + { + get { return MInput.GamePads[GamepadIndex].LeftStickLeftCheck(Deadzone); } + } + + public override bool Pressed + { + get { return MInput.GamePads[GamepadIndex].LeftStickLeftPressed(Deadzone); } + } + + public override bool Released + { + get { return MInput.GamePads[GamepadIndex].LeftStickLeftReleased(Deadzone); } + } + } + + public class PadLeftStickUp : Node + { + public int GamepadIndex; + public float Deadzone; + + public PadLeftStickUp(int gamepadindex, float deadzone) + { + GamepadIndex = gamepadindex; + Deadzone = deadzone; + } + + public override bool Check + { + get { return MInput.GamePads[GamepadIndex].LeftStickUpCheck(Deadzone); } + } + + public override bool Pressed + { + get { return MInput.GamePads[GamepadIndex].LeftStickUpPressed(Deadzone); } + } + + public override bool Released + { + get { return MInput.GamePads[GamepadIndex].LeftStickUpReleased(Deadzone); } + } + } + + public class PadLeftStickDown : Node + { + public int GamepadIndex; + public float Deadzone; + + public PadLeftStickDown(int gamepadindex, float deadzone) + { + GamepadIndex = gamepadindex; + Deadzone = deadzone; + } + + public override bool Check + { + get { return MInput.GamePads[GamepadIndex].LeftStickDownCheck(Deadzone); } + } + + public override bool Pressed + { + get { return MInput.GamePads[GamepadIndex].LeftStickDownPressed(Deadzone); } + } + + public override bool Released + { + get { return MInput.GamePads[GamepadIndex].LeftStickDownReleased(Deadzone); } + } + } + + #endregion + + #region Pad Right Stick + + public class PadRightStickRight : Node + { + public int GamepadIndex; + public float Deadzone; + + public PadRightStickRight(int gamepadindex, float deadzone) + { + GamepadIndex = gamepadindex; + Deadzone = deadzone; + } + + public override bool Check + { + get { return MInput.GamePads[GamepadIndex].RightStickRightCheck(Deadzone); } + } + + public override bool Pressed + { + get { return MInput.GamePads[GamepadIndex].RightStickRightPressed(Deadzone); } + } + + public override bool Released + { + get { return MInput.GamePads[GamepadIndex].RightStickRightReleased(Deadzone); } + } + } + + public class PadRightStickLeft : Node + { + public int GamepadIndex; + public float Deadzone; + + public PadRightStickLeft(int gamepadindex, float deadzone) + { + GamepadIndex = gamepadindex; + Deadzone = deadzone; + } + + public override bool Check + { + get { return MInput.GamePads[GamepadIndex].RightStickLeftCheck(Deadzone); } + } + + public override bool Pressed + { + get { return MInput.GamePads[GamepadIndex].RightStickLeftPressed(Deadzone); } + } + + public override bool Released + { + get { return MInput.GamePads[GamepadIndex].RightStickLeftReleased(Deadzone); } + } + } + + public class PadRightStickUp : Node + { + public int GamepadIndex; + public float Deadzone; + + public PadRightStickUp(int gamepadindex, float deadzone) + { + GamepadIndex = gamepadindex; + Deadzone = deadzone; + } + + public override bool Check + { + get { return MInput.GamePads[GamepadIndex].RightStickUpCheck(Deadzone); } + } + + public override bool Pressed + { + get { return MInput.GamePads[GamepadIndex].RightStickUpPressed(Deadzone); } + } + + public override bool Released + { + get { return MInput.GamePads[GamepadIndex].RightStickUpReleased(Deadzone); } + } + } + + public class PadRightStickDown : Node + { + public int GamepadIndex; + public float Deadzone; + + public PadRightStickDown(int gamepadindex, float deadzone) + { + GamepadIndex = gamepadindex; + Deadzone = deadzone; + } + + public override bool Check + { + get { return MInput.GamePads[GamepadIndex].RightStickDownCheck(Deadzone); } + } + + public override bool Pressed + { + get { return MInput.GamePads[GamepadIndex].RightStickDownPressed(Deadzone); } + } + + public override bool Released + { + get { return MInput.GamePads[GamepadIndex].RightStickDownReleased(Deadzone); } + } + } + + #endregion + + #region Pad Triggers + + public class PadLeftTrigger : Node + { + public int GamepadIndex; + public float Threshold; + + public PadLeftTrigger(int gamepadIndex, float threshold) + { + GamepadIndex = gamepadIndex; + Threshold = threshold; + } + + public override bool Check + { + get { return MInput.GamePads[GamepadIndex].LeftTriggerCheck(Threshold); } + } + + public override bool Pressed + { + get { return MInput.GamePads[GamepadIndex].LeftTriggerPressed(Threshold); } + } + + public override bool Released + { + get { return MInput.GamePads[GamepadIndex].LeftTriggerReleased(Threshold); } + } + } + + public class PadRightTrigger : Node + { + public int GamepadIndex; + public float Threshold; + + public PadRightTrigger(int gamepadIndex, float threshold) + { + GamepadIndex = gamepadIndex; + Threshold = threshold; + } + + public override bool Check + { + get { return MInput.GamePads[GamepadIndex].RightTriggerCheck(Threshold); } + } + + public override bool Pressed + { + get { return MInput.GamePads[GamepadIndex].RightTriggerPressed(Threshold); } + } + + public override bool Released + { + get { return MInput.GamePads[GamepadIndex].RightTriggerReleased(Threshold); } + } + } + + #endregion + + #region Pad DPad + + public class PadDPadRight : Node + { + public int GamepadIndex; + + public PadDPadRight(int gamepadIndex) + { + GamepadIndex = gamepadIndex; + } + + public override bool Check + { + get { return MInput.GamePads[GamepadIndex].DPadRightCheck; } + } + + public override bool Pressed + { + get { return MInput.GamePads[GamepadIndex].DPadRightPressed; } + } + + public override bool Released + { + get { return MInput.GamePads[GamepadIndex].DPadRightReleased; } + } + } + + public class PadDPadLeft : Node + { + public int GamepadIndex; + + public PadDPadLeft(int gamepadIndex) + { + GamepadIndex = gamepadIndex; + } + + public override bool Check + { + get { return MInput.GamePads[GamepadIndex].DPadLeftCheck; } + } + + public override bool Pressed + { + get { return MInput.GamePads[GamepadIndex].DPadLeftPressed; } + } + + public override bool Released + { + get { return MInput.GamePads[GamepadIndex].DPadLeftReleased; } + } + } + + public class PadDPadUp : Node + { + public int GamepadIndex; + + public PadDPadUp(int gamepadIndex) + { + GamepadIndex = gamepadIndex; + } + + public override bool Check + { + get { return MInput.GamePads[GamepadIndex].DPadUpCheck; } + } + + public override bool Pressed + { + get { return MInput.GamePads[GamepadIndex].DPadUpPressed; } + } + + public override bool Released + { + get { return MInput.GamePads[GamepadIndex].DPadUpReleased; } + } + } + + public class PadDPadDown : Node + { + public int GamepadIndex; + + public PadDPadDown(int gamepadIndex) + { + GamepadIndex = gamepadIndex; + } + + public override bool Check + { + get { return MInput.GamePads[GamepadIndex].DPadDownCheck; } + } + + public override bool Pressed + { + get { return MInput.GamePads[GamepadIndex].DPadDownPressed; } + } + + public override bool Released + { + get { return MInput.GamePads[GamepadIndex].DPadDownReleased; } + } + } + + #endregion + + #region Mouse + + public class MouseLeftButton : Node + { + public override bool Check + { + get { return MInput.Mouse.CheckLeftButton; } + } + + public override bool Pressed + { + get { return MInput.Mouse.PressedLeftButton; } + } + + public override bool Released + { + get { return MInput.Mouse.ReleasedLeftButton; } + } + } + + public class MouseRightButton : Node + { + public override bool Check + { + get { return MInput.Mouse.CheckRightButton; } + } + + public override bool Pressed + { + get { return MInput.Mouse.PressedRightButton; } + } + + public override bool Released + { + get { return MInput.Mouse.ReleasedRightButton; } + } + } + + public class MouseMiddleButton : Node + { + public override bool Check + { + get { return MInput.Mouse.CheckMiddleButton; } + } + + public override bool Pressed + { + get { return MInput.Mouse.PressedMiddleButton; } + } + + public override bool Released + { + get { return MInput.Mouse.ReleasedMiddleButton; } + } + } + + #endregion + + #region Other Virtual Inputs + + public class VirtualAxisTrigger : Node + { + public enum Modes { LargerThan, LessThan, Equals }; + + public VirtualInput.ThresholdModes Mode; + public float Threshold; + + private VirtualAxis axis; + + public VirtualAxisTrigger(VirtualAxis axis, VirtualInput.ThresholdModes mode, float threshold) + { + this.axis = axis; + Mode = mode; + Threshold = threshold; + } + + public override bool Check + { + get + { + if (Mode == VirtualInput.ThresholdModes.LargerThan) + return axis.Value >= Threshold; + else if (Mode == VirtualInput.ThresholdModes.LessThan) + return axis.Value <= Threshold; + else + return axis.Value == Threshold; + } + } + + public override bool Pressed + { + get + { + if (Mode == VirtualInput.ThresholdModes.LargerThan) + return axis.Value >= Threshold && axis.PreviousValue < Threshold; + else if (Mode == VirtualInput.ThresholdModes.LessThan) + return axis.Value <= Threshold && axis.PreviousValue > Threshold; + else + return axis.Value == Threshold && axis.PreviousValue != Threshold; + } + } + + public override bool Released + { + get + { + if (Mode == VirtualInput.ThresholdModes.LargerThan) + return axis.Value < Threshold && axis.PreviousValue >= Threshold; + else if (Mode == VirtualInput.ThresholdModes.LessThan) + return axis.Value > Threshold && axis.PreviousValue <= Threshold; + else + return axis.Value != Threshold && axis.PreviousValue == Threshold; + } + } + } + + public class VirtualIntegerAxisTrigger : Node + { + public enum Modes { LargerThan, LessThan, Equals }; + + public VirtualInput.ThresholdModes Mode; + public int Threshold; + + private VirtualIntegerAxis axis; + + public VirtualIntegerAxisTrigger(VirtualIntegerAxis axis, VirtualInput.ThresholdModes mode, int threshold) + { + this.axis = axis; + Mode = mode; + Threshold = threshold; + } + + public override bool Check + { + get + { + if (Mode == VirtualInput.ThresholdModes.LargerThan) + return axis.Value >= Threshold; + else if (Mode == VirtualInput.ThresholdModes.LessThan) + return axis.Value <= Threshold; + else + return axis.Value == Threshold; + } + } + + public override bool Pressed + { + get + { + if (Mode == VirtualInput.ThresholdModes.LargerThan) + return axis.Value >= Threshold && axis.PreviousValue < Threshold; + else if (Mode == VirtualInput.ThresholdModes.LessThan) + return axis.Value <= Threshold && axis.PreviousValue > Threshold; + else + return axis.Value == Threshold && axis.PreviousValue != Threshold; + } + } + + public override bool Released + { + get + { + if (Mode == VirtualInput.ThresholdModes.LargerThan) + return axis.Value < Threshold && axis.PreviousValue >= Threshold; + else if (Mode == VirtualInput.ThresholdModes.LessThan) + return axis.Value > Threshold && axis.PreviousValue <= Threshold; + else + return axis.Value != Threshold && axis.PreviousValue == Threshold; + } + } + } + + public class VirtualJoystickXTrigger : Node + { + public enum Modes { LargerThan, LessThan, Equals }; + + public VirtualInput.ThresholdModes Mode; + public float Threshold; + + private VirtualJoystick joystick; + + public VirtualJoystickXTrigger(VirtualJoystick joystick, VirtualInput.ThresholdModes mode, float threshold) + { + this.joystick = joystick; + Mode = mode; + Threshold = threshold; + } + + public override bool Check + { + get + { + if (Mode == VirtualInput.ThresholdModes.LargerThan) + return joystick.Value.X >= Threshold; + else if (Mode == VirtualInput.ThresholdModes.LessThan) + return joystick.Value.X <= Threshold; + else + return joystick.Value.X == Threshold; + } + } + + public override bool Pressed + { + get + { + if (Mode == VirtualInput.ThresholdModes.LargerThan) + return joystick.Value.X >= Threshold && joystick.PreviousValue.X < Threshold; + else if (Mode == VirtualInput.ThresholdModes.LessThan) + return joystick.Value.X <= Threshold && joystick.PreviousValue.X > Threshold; + else + return joystick.Value.X == Threshold && joystick.PreviousValue.X != Threshold; + } + } + + public override bool Released + { + get + { + if (Mode == VirtualInput.ThresholdModes.LargerThan) + return joystick.Value.X < Threshold && joystick.PreviousValue.X >= Threshold; + else if (Mode == VirtualInput.ThresholdModes.LessThan) + return joystick.Value.X > Threshold && joystick.PreviousValue.X <= Threshold; + else + return joystick.Value.X != Threshold && joystick.PreviousValue.X == Threshold; + } + } + } + + public class VirtualJoystickYTrigger : Node + { + public VirtualInput.ThresholdModes Mode; + public float Threshold; + + private VirtualJoystick joystick; + + public VirtualJoystickYTrigger(VirtualJoystick joystick, VirtualInput.ThresholdModes mode, float threshold) + { + this.joystick = joystick; + Mode = mode; + Threshold = threshold; + } + + public override bool Check + { + get + { + if (Mode == VirtualInput.ThresholdModes.LargerThan) + return joystick.Value.X >= Threshold; + else if (Mode == VirtualInput.ThresholdModes.LessThan) + return joystick.Value.X <= Threshold; + else + return joystick.Value.X == Threshold; + } + } + + public override bool Pressed + { + get + { + if (Mode == VirtualInput.ThresholdModes.LargerThan) + return joystick.Value.X >= Threshold && joystick.PreviousValue.X < Threshold; + else if (Mode == VirtualInput.ThresholdModes.LessThan) + return joystick.Value.X <= Threshold && joystick.PreviousValue.X > Threshold; + else + return joystick.Value.X == Threshold && joystick.PreviousValue.X != Threshold; + } + } + + public override bool Released + { + get + { + if (Mode == VirtualInput.ThresholdModes.LargerThan) + return joystick.Value.X < Threshold && joystick.PreviousValue.X >= Threshold; + else if (Mode == VirtualInput.ThresholdModes.LessThan) + return joystick.Value.X > Threshold && joystick.PreviousValue.X <= Threshold; + else + return joystick.Value.X != Threshold && joystick.PreviousValue.X == Threshold; + } + } + } + + #endregion + } +} diff --git a/MonocleEngineDemo/Monocle/Input/VirtualInput.cs b/MonocleEngineDemo/Monocle/Input/VirtualInput.cs new file mode 100644 index 0000000..99d49a3 --- /dev/null +++ b/MonocleEngineDemo/Monocle/Input/VirtualInput.cs @@ -0,0 +1,36 @@ + +namespace Monocle +{ + /// + /// Represents a virtual button, axis or joystick whose state is determined by the state of its VirtualInputNodes + /// + public abstract class VirtualInput + { + public enum OverlapBehaviors { CancelOut, TakeOlder, TakeNewer }; + public enum ThresholdModes { LargerThan, LessThan, EqualTo }; + + public VirtualInput() + { + MInput.VirtualInputs.Add(this); + } + + public void Deregister() + { + MInput.VirtualInputs.Remove(this); + } + + public abstract void Update(); + } + + /// + /// Add these to your VirtualInput to define how it determines its current input state. + /// For example, if you want to check whether a keyboard key is pressed, create a VirtualButton and add to it a VirtualButton.KeyboardKey + /// + public abstract class VirtualInputNode + { + public virtual void Update() + { + + } + } +} diff --git a/MonocleEngineDemo/Monocle/Input/VirtualIntegerAxis.cs b/MonocleEngineDemo/Monocle/Input/VirtualIntegerAxis.cs new file mode 100644 index 0000000..50103b3 --- /dev/null +++ b/MonocleEngineDemo/Monocle/Input/VirtualIntegerAxis.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; + +namespace Monocle +{ + /// + /// A virtual input that is represented as a int that is either -1, 0, or 1 + /// + public class VirtualIntegerAxis : VirtualInput + { + public List Nodes; + + public int Value; + public int PreviousValue { get; private set; } + + public VirtualIntegerAxis() + : base() + { + Nodes = new List(); + } + + public VirtualIntegerAxis(params VirtualAxis.Node[] nodes) + : base() + { + Nodes = new List(nodes); + } + + public override void Update() + { + foreach (var node in Nodes) + node.Update(); + + PreviousValue = Value; + Value = 0; + foreach (var node in Nodes) + { + float value = node.Value; + if (value != 0) + { + Value = Math.Sign(value); + break; + } + } + } + + public static implicit operator int(VirtualIntegerAxis axis) + { + return axis.Value; + } + } +} diff --git a/MonocleEngineDemo/Monocle/Input/VirtualJoystick.cs b/MonocleEngineDemo/Monocle/Input/VirtualJoystick.cs new file mode 100644 index 0000000..ee23201 --- /dev/null +++ b/MonocleEngineDemo/Monocle/Input/VirtualJoystick.cs @@ -0,0 +1,257 @@ +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Input; +using System.Collections.Generic; + +namespace Monocle +{ + /// + /// A virtual input that is represented as a Vector2, with both X and Y as values between -1 and 1 + /// + public class VirtualJoystick : VirtualInput + { + public List Nodes; + public bool Normalized; + public float? SnapSlices; + + public Vector2 Value { get; private set; } + public Vector2 PreviousValue { get; private set; } + + public VirtualJoystick(bool normalized) + : base() + { + Nodes = new List(); + Normalized = normalized; + } + + public VirtualJoystick(bool normalized, params Node[] nodes) + : base() + { + Nodes = new List(nodes); + Normalized = normalized; + } + + public override void Update() + { + foreach (var node in Nodes) + node.Update(); + + PreviousValue = Value; + Value = Vector2.Zero; + foreach (var node in Nodes) + { + Vector2 value = node.Value; + if (value != Vector2.Zero) + { + if (Normalized) + { + if (SnapSlices.HasValue) + value = value.SnappedNormal(SnapSlices.Value); + else + value.Normalize(); + } + else if (SnapSlices.HasValue) + value = value.Snapped(SnapSlices.Value); + + Value = value; + break; + } + } + } + + public static implicit operator Vector2(VirtualJoystick joystick) + { + return joystick.Value; + } + + public abstract class Node : VirtualInputNode + { + public abstract Vector2 Value { get; } + } + + public class PadLeftStick : Node + { + public int GamepadIndex; + public float Deadzone; + + public PadLeftStick(int gamepadIndex, float deadzone) + { + GamepadIndex = gamepadIndex; + Deadzone = deadzone; + } + + public override Vector2 Value + { + get + { + return MInput.GamePads[GamepadIndex].GetLeftStick(Deadzone); + } + } + } + + public class PadRightStick : Node + { + public int GamepadIndex; + public float Deadzone; + + public PadRightStick(int gamepadIndex, float deadzone) + { + GamepadIndex = gamepadIndex; + Deadzone = deadzone; + } + + public override Vector2 Value + { + get + { + return MInput.GamePads[GamepadIndex].GetRightStick(Deadzone); + } + } + } + + public class PadDpad : Node + { + public int GamepadIndex; + + public PadDpad(int gamepadIndex) + { + GamepadIndex = gamepadIndex; + } + + public override Vector2 Value + { + get + { + Vector2 value = Vector2.Zero; + + if (MInput.GamePads[GamepadIndex].DPadRightCheck) + value.X = 1f; + else if (MInput.GamePads[GamepadIndex].DPadLeftCheck) + value.X = -1f; + + if (MInput.GamePads[GamepadIndex].DPadDownCheck) + value.Y = 1f; + else if (MInput.GamePads[GamepadIndex].DPadUpCheck) + value.Y = -1f; + + return value; + } + } + } + + public class KeyboardKeys : Node + { + public OverlapBehaviors OverlapBehavior; + public Keys Left; + public Keys Right; + public Keys Up; + public Keys Down; + + private bool turnedX; + private bool turnedY; + private Vector2 value; + + public KeyboardKeys(OverlapBehaviors overlapBehavior, Keys left, Keys right, Keys up, Keys down) + { + OverlapBehavior = overlapBehavior; + Left = left; + Right = right; + Up = up; + Down = down; + } + + public override void Update() + { + //X Axis + if (MInput.Keyboard.Check(Left)) + { + if (MInput.Keyboard.Check(Right)) + { + switch (OverlapBehavior) + { + default: + case OverlapBehaviors.CancelOut: + value.X = 0; + break; + + case OverlapBehaviors.TakeNewer: + if (!turnedX) + { + value.X *= -1; + turnedX = true; + } + break; + + case OverlapBehaviors.TakeOlder: + //X stays the same + break; + } + } + else + { + turnedX = false; + value.X = -1; + } + } + else if (MInput.Keyboard.Check(Right)) + { + turnedX = false; + value.X = 1; + } + else + { + turnedX = false; + value.X = 0; + } + + //Y Axis + if (MInput.Keyboard.Check(Up)) + { + if (MInput.Keyboard.Check(Down)) + { + switch (OverlapBehavior) + { + default: + case OverlapBehaviors.CancelOut: + value.Y = 0; + break; + + case OverlapBehaviors.TakeNewer: + if (!turnedY) + { + value.Y *= -1; + turnedY = true; + } + break; + + case OverlapBehaviors.TakeOlder: + //Y stays the same + break; + } + } + else + { + turnedY = false; + value.Y = -1; + } + } + else if (MInput.Keyboard.Check(Down)) + { + turnedY = false; + value.Y = 1; + } + else + { + turnedY = false; + value.Y = 0; + } + } + + public override Vector2 Value + { + get { return value; } + } + } + } + + +} diff --git a/MonocleEngineDemo/Monocle/InternalUtilities/ComponentList.cs b/MonocleEngineDemo/Monocle/InternalUtilities/ComponentList.cs new file mode 100644 index 0000000..4a6a0e3 --- /dev/null +++ b/MonocleEngineDemo/Monocle/InternalUtilities/ComponentList.cs @@ -0,0 +1,252 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; + +namespace Monocle +{ + public class ComponentList : IEnumerable, IEnumerable + { + public enum LockModes { Open, Locked, Error }; + public Entity Entity { get; internal set; } + + private List components; + private List toAdd; + private List toRemove; + + private HashSet current; + private HashSet adding; + private HashSet removing; + + private LockModes lockMode; + + internal ComponentList(Entity entity) + { + Entity = entity; + + components = new List(); + toAdd = new List(); + toRemove = new List(); + current = new HashSet(); + adding = new HashSet(); + removing = new HashSet(); + } + + internal LockModes LockMode + { + get + { + return lockMode; + } + + set + { + lockMode = value; + + if (toAdd.Count > 0) + { + foreach (var component in toAdd) + { + if (!current.Contains(component)) + { + current.Add(component); + components.Add(component); + component.Added(Entity); + } + } + + adding.Clear(); + toAdd.Clear(); + } + + if (toRemove.Count > 0) + { + foreach (var component in toRemove) + { + if (current.Contains(component)) + { + current.Remove(component); + components.Remove(component); + component.Removed(Entity); + } + } + + removing.Clear(); + toRemove.Clear(); + } + } + } + + public void Add(Component component) + { + switch (lockMode) + { + case LockModes.Open: + if (!current.Contains(component)) + { + current.Add(component); + components.Add(component); + component.Added(Entity); + } + break; + + case LockModes.Locked: + if (!current.Contains(component) && !adding.Contains(component)) + { + adding.Add(component); + toAdd.Add(component); + } + break; + + case LockModes.Error: + throw new Exception("Cannot add or remove Entities at this time!"); + } + } + + public void Remove(Component component) + { + switch (lockMode) + { + case LockModes.Open: + if (current.Contains(component)) + { + current.Remove(component); + components.Remove(component); + component.Removed(Entity); + } + break; + + case LockModes.Locked: + if (current.Contains(component) && !removing.Contains(component)) + { + removing.Add(component); + toRemove.Add(component); + } + break; + + case LockModes.Error: + throw new Exception("Cannot add or remove Entities at this time!"); + } + } + + public void Add(IEnumerable components) + { + foreach (var component in components) + Add(component); + } + + public void Remove(IEnumerable components) + { + foreach (var component in components) + Remove(component); + } + + public void RemoveAll() where T : Component + { + Remove(GetAll()); + } + + public void Add(params Component[] components) + { + foreach (var component in components) + Add(component); + } + + public void Remove(params Component[] components) + { + foreach (var component in components) + Remove(component); + } + + public int Count + { + get + { + return components.Count; + } + } + + public Component this[int index] + { + get + { + if (index < 0 || index >= components.Count) + throw new IndexOutOfRangeException(); + else + return components[index]; + } + } + + public IEnumerator GetEnumerator() + { + return components.GetEnumerator(); + } + + IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public Component[] ToArray() + { + return components.ToArray(); + } + + internal void Update() + { + LockMode = ComponentList.LockModes.Locked; + foreach (var component in components) + if (component.Active) + component.Update(); + LockMode = ComponentList.LockModes.Open; + } + + internal void Render() + { + LockMode = ComponentList.LockModes.Error; + foreach (var component in components) + if (component.Visible) + component.Render(); + LockMode = ComponentList.LockModes.Open; + } + + internal void DebugRender(Camera camera) + { + LockMode = ComponentList.LockModes.Error; + foreach (var component in components) + component.DebugRender(camera); + LockMode = ComponentList.LockModes.Open; + } + + internal void HandleGraphicsReset() + { + LockMode = ComponentList.LockModes.Error; + foreach (var component in components) + component.HandleGraphicsReset(); + LockMode = ComponentList.LockModes.Open; + } + + internal void HandleGraphicsCreate() + { + LockMode = ComponentList.LockModes.Error; + foreach (var component in components) + component.HandleGraphicsCreate(); + LockMode = ComponentList.LockModes.Open; + } + + public T Get() where T : Component + { + foreach (var component in components) + if (component is T) + return component as T; + return null; + } + + public IEnumerable GetAll() where T : Component + { + foreach (var component in components) + if (component is T) + yield return component as T; + } + } +} diff --git a/MonocleEngineDemo/Monocle/InternalUtilities/EntityList.cs b/MonocleEngineDemo/Monocle/InternalUtilities/EntityList.cs new file mode 100644 index 0000000..d00b1ee --- /dev/null +++ b/MonocleEngineDemo/Monocle/InternalUtilities/EntityList.cs @@ -0,0 +1,285 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; + +namespace Monocle +{ + public class EntityList : IEnumerable, IEnumerable + { + public Scene Scene { get; private set; } + + private List entities; + private List toAdd; + private List toAwake; + private List toRemove; + + private HashSet current; + private HashSet adding; + private HashSet removing; + + private bool unsorted; + + internal EntityList(Scene scene) + { + Scene = scene; + + entities = new List(); + toAdd = new List(); + toAwake = new List(); + toRemove = new List(); + + current = new HashSet(); + adding = new HashSet(); + removing = new HashSet(); + } + + internal void MarkUnsorted() + { + unsorted = true; + } + + public void UpdateLists() + { + if (toAdd.Count > 0) + { + for (int i = 0; i < toAdd.Count; i++) + { + var entity = toAdd[i]; + if (!current.Contains(entity)) + { + current.Add(entity); + entities.Add(entity); + + if (Scene != null) + { + Scene.TagLists.EntityAdded(entity); + Scene.Tracker.EntityAdded(entity); + entity.Added(Scene); + } + } + } + + unsorted = true; + } + + if (toRemove.Count > 0) + { + for (int i = 0; i < toRemove.Count; i++) + { + var entity = toRemove[i]; + if (entities.Contains(entity)) + { + current.Remove(entity); + entities.Remove(entity); + + if (Scene != null) + { + entity.Removed(Scene); + Scene.TagLists.EntityRemoved(entity); + Scene.Tracker.EntityRemoved(entity); + Engine.Pooler.EntityRemoved(entity); + } + } + } + + toRemove.Clear(); + removing.Clear(); + } + + if (unsorted) + { + unsorted = false; + entities.Sort(CompareDepth); + } + + if (toAdd.Count > 0) + { + toAwake.AddRange(toAdd); + toAdd.Clear(); + adding.Clear(); + + foreach (var entity in toAwake) + if (entity.Scene == Scene) + entity.Awake(Scene); + toAwake.Clear(); + } + } + + public void Add(Entity entity) + { + if (!adding.Contains(entity) && !current.Contains(entity)) + { + adding.Add(entity); + toAdd.Add(entity); + } + } + + public void Remove(Entity entity) + { + if (!removing.Contains(entity) && current.Contains(entity)) + { + removing.Add(entity); + toRemove.Add(entity); + } + } + + public void Add(IEnumerable entities) + { + foreach (var entity in entities) + Add(entity); + } + + public void Remove(IEnumerable entities) + { + foreach (var entity in entities) + Remove(entity); + } + + public void Add(params Entity[] entities) + { + for (int i = 0; i < entities.Length; i++) + Add(entities[i]); + } + + public void Remove(params Entity[] entities) + { + for (int i = 0; i < entities.Length; i++) + Remove(entities[i]); + } + + public int Count + { + get + { + return entities.Count; + } + } + + public Entity this[int index] + { + get + { + if (index < 0 || index >= entities.Count) + throw new IndexOutOfRangeException(); + else + return entities[index]; + } + } + + public int AmountOf() where T : Entity + { + int count = 0; + foreach (var e in entities) + if (e is T) + count++; + + return count; + } + + public T FindFirst() where T : Entity + { + foreach (var e in entities) + if (e is T) + return e as T; + + return null; + } + + public List FindAll() where T : Entity + { + List list = new List(); + + foreach (var e in entities) + if (e is T) + list.Add(e as T); + + return list; + } + + public void With(Action action) where T : Entity + { + foreach (var e in entities) + if (e is T) + action(e as T); + } + + public IEnumerator GetEnumerator() + { + return entities.GetEnumerator(); + } + + IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public Entity[] ToArray() + { + return entities.ToArray(); + } + + public bool HasVisibleEntities(int matchTags) + { + foreach (var entity in entities) + if (entity.Visible && entity.TagCheck(matchTags)) + return true; + return false; + } + + internal void Update() + { + foreach (var entity in entities) + if (entity.Active) + entity.Update(); + } + + public void Render() + { + foreach (var entity in entities) + if (entity.Visible) + entity.Render(); + } + + public void RenderOnly(int matchTags) + { + foreach (var entity in entities) + if (entity.Visible && entity.TagCheck(matchTags)) + entity.Render(); + } + + public void RenderOnlyFullMatch(int matchTags) + { + foreach (var entity in entities) + if (entity.Visible && entity.TagFullCheck(matchTags)) + entity.Render(); + } + + public void RenderExcept(int excludeTags) + { + foreach (var entity in entities) + if (entity.Visible && !entity.TagCheck(excludeTags)) + entity.Render(); + } + + public void DebugRender(Camera camera) + { + foreach (var entity in entities) + entity.DebugRender(camera); + } + + internal void HandleGraphicsReset() + { + foreach (var entity in entities) + entity.HandleGraphicsReset(); + } + + internal void HandleGraphicsCreate() + { + foreach (var entity in entities) + entity.HandleGraphicsCreate(); + } + + public static Comparison CompareDepth = (a, b) => { return Math.Sign(b.actualDepth - a.actualDepth); }; + } +} diff --git a/MonocleEngineDemo/Monocle/InternalUtilities/RendererList.cs b/MonocleEngineDemo/Monocle/InternalUtilities/RendererList.cs new file mode 100644 index 0000000..f4353b4 --- /dev/null +++ b/MonocleEngineDemo/Monocle/InternalUtilities/RendererList.cs @@ -0,0 +1,93 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Monocle +{ + public class RendererList + { + public List Renderers; + private List adding; + private List removing; + private Scene scene; + + internal RendererList(Scene scene) + { + this.scene = scene; + + Renderers = new List(); + adding = new List(); + removing = new List(); + } + + internal void UpdateLists() + { + if (adding.Count > 0) + foreach (var renderer in adding) + Renderers.Add(renderer); + adding.Clear(); + if (removing.Count > 0) + foreach (var renderer in removing) + Renderers.Remove(renderer); + removing.Clear(); + } + + internal void Update() + { + foreach (var renderer in Renderers) + renderer.Update(scene); + } + + internal void BeforeRender() + { + for (int i = 0; i < Renderers.Count; i++) + { + if (!Renderers[i].Visible) + continue; + Draw.Renderer = Renderers[i]; + Renderers[i].BeforeRender(scene); + } + } + + internal void Render() + { + for (int i = 0; i < Renderers.Count; i++) + { + if (!Renderers[i].Visible) + continue; + Draw.Renderer = Renderers[i]; + Renderers[i].Render(scene); + } + } + + internal void AfterRender() + { + for (int i = 0; i < Renderers.Count; i++) + { + if (!Renderers[i].Visible) + continue; + Draw.Renderer = Renderers[i]; + Renderers[i].AfterRender(scene); + } + } + + public void MoveToFront(Renderer renderer) + { + Renderers.Remove(renderer); + Renderers.Add(renderer); + } + + public void Add(Renderer renderer) + { + adding.Add(renderer); + } + + public void Remove(Renderer renderer) + { + removing.Add(renderer); + } + + } +} diff --git a/MonocleEngineDemo/Monocle/InternalUtilities/TagLists.cs b/MonocleEngineDemo/Monocle/InternalUtilities/TagLists.cs new file mode 100644 index 0000000..53cd233 --- /dev/null +++ b/MonocleEngineDemo/Monocle/InternalUtilities/TagLists.cs @@ -0,0 +1,74 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Monocle +{ + public class TagLists + { + private List[] lists; + private bool[] unsorted; + private bool areAnyUnsorted; + + internal TagLists() + { + lists = new List[BitTag.TotalTags]; + unsorted = new bool[BitTag.TotalTags]; + for (int i = 0; i < lists.Length; i++) + lists[i] = new List(); + } + + public List this[int index] + { + get + { + return lists[index]; + } + } + + internal void MarkUnsorted(int index) + { + areAnyUnsorted = true; + unsorted[index] = true; + } + + internal void UpdateLists() + { + if (areAnyUnsorted) + { + for (int i = 0; i < lists.Length; i++) + { + if (unsorted[i]) + { + lists[i].Sort(EntityList.CompareDepth); + unsorted[i] = false; + } + } + + areAnyUnsorted = false; + } + } + + internal void EntityAdded(Entity entity) + { + for (int i = 0; i < BitTag.TotalTags; i++) + { + if (entity.TagCheck(1 << i)) + { + this[i].Add(entity); + areAnyUnsorted = true; + unsorted[i] = true; + } + } + } + + internal void EntityRemoved(Entity entity) + { + for (int i = 0; i < BitTag.TotalTags; i++) + if (entity.TagCheck(1 << i)) + lists[i].Remove(entity); + } + } +} diff --git a/MonocleEngineDemo/Monocle/Monocle.MonoGame.csproj b/MonocleEngineDemo/Monocle/Monocle.MonoGame.csproj new file mode 100644 index 0000000..593841b --- /dev/null +++ b/MonocleEngineDemo/Monocle/Monocle.MonoGame.csproj @@ -0,0 +1,132 @@ + + + + + Debug + AnyCPU + {6EC3C0A6-C5BE-42CE-AEA5-881D8A97EB60} + Library + Properties + Monocle + Monocle + v4.5.2 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Always + + + + + \ No newline at end of file diff --git a/MonocleEngineDemo/Monocle/Monocle.csproj b/MonocleEngineDemo/Monocle/Monocle.csproj new file mode 100644 index 0000000..6653446 --- /dev/null +++ b/MonocleEngineDemo/Monocle/Monocle.csproj @@ -0,0 +1,185 @@ + + + + {25D1847C-BCB0-4D6F-BBA5-CD1DC9B8D295} + {6D335F3A-9D43-41b4-9D22-F6F17C4BE596};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + Debug + x86 + Library + Properties + Monocle + Monocle + v4.5.2 + + v4.0 + Windows + HiDef + 4e152ded-4ea8-416f-aabf-dd49c36c042c + Library + + + true + full + false + bin\x86\Debug + DEBUG;TRACE;WINDOWS + prompt + 4 + true + false + x86 + false + + + pdbonly + true + bin\x86\Release + TRACE;WINDOWS + prompt + 4 + true + false + x86 + true + + + true + bin\x86\SteamDebug\ + DEBUG;TRACE;WINDOWS + true + full + x86 + false + prompt + MinimumRecommendedRules.ruleset + + + bin\x86\SteamRelease\ + TRACE;WINDOWS + true + true + pdbonly + x86 + false + prompt + MinimumRecommendedRules.ruleset + + + + + + + False + + + False + + + False + + + 4.0 + False + + + 4.0 + False + + + False + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Always + + + + + + \ No newline at end of file diff --git a/MonocleEngineDemo/Monocle/Particles/Particle.cs b/MonocleEngineDemo/Monocle/Particles/Particle.cs new file mode 100644 index 0000000..f3a8511 --- /dev/null +++ b/MonocleEngineDemo/Monocle/Particles/Particle.cs @@ -0,0 +1,139 @@ +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using System; + +namespace Monocle +{ + public struct Particle + { + public Entity Track; + public ParticleType Type; + public MTexture Source; + + public bool Active; + public Color Color; + public Color StartColor; + public Vector2 Position; + public Vector2 Speed; + public float Size; + public float StartSize; + public float Life; + public float StartLife; + public float ColorSwitch; + public float Rotation; + public float Spin; + + public bool SimulateFor(float duration) + { + if (duration > Life) + { + Life = 0; + Active = false; + return false; + } + else + { + var dt = Engine.TimeRate * (Engine.Instance.TargetElapsedTime.Milliseconds / 1000f); + if (dt > 0) + for (var t = 0f; t < duration; t += dt) + Update(dt); + + return true; + } + } + + public void Update(float? delta = null) + { + var dt = 0f; + if (delta.HasValue) + dt = delta.Value; + else + dt = (Type.UseActualDeltaTime ? Engine.RawDeltaTime : Engine.DeltaTime); + + var ease = Life / StartLife; + + //Life + Life -= dt; + if (Life <= 0) + { + Active = false; + return; + } + + //Spin + if (Type.RotationMode == ParticleType.RotationModes.SameAsDirection) + { + if (Speed != Vector2.Zero) + Rotation = Speed.Angle(); + } + else + Rotation += Spin * dt; + + //Fade + float alpha; + if (Type.FadeMode == ParticleType.FadeModes.Linear) + alpha = ease; + else if (Type.FadeMode == ParticleType.FadeModes.Late) + alpha = Math.Min(1f, ease / .25f); + else if (Type.FadeMode == ParticleType.FadeModes.InAndOut) + { + if (ease > .75f) + alpha = 1 - ((ease - .75f) / .25f); + else if (ease < .25f) + alpha = ease / .25f; + else + alpha = 1f; + } + else + alpha = 1f; + + + //Color switch with alpha + if (alpha == 0) + Color = Color.Transparent; + else + { + if (Type.ColorMode == ParticleType.ColorModes.Static) + Color = StartColor; + else if (Type.ColorMode == ParticleType.ColorModes.Fade) + Color = Color.Lerp(Type.Color2, StartColor, ease); + else if (Type.ColorMode == ParticleType.ColorModes.Blink) + Color = (Calc.BetweenInterval(Life, .1f) ? StartColor : Type.Color2); + else if (Type.ColorMode == ParticleType.ColorModes.Choose) + Color = StartColor; + + if (alpha < 1f) + Color *= alpha; + } + + //Speed + Position += Speed * dt; + Speed += Type.Acceleration * dt; + Speed = Calc.Approach(Speed, Vector2.Zero, Type.Friction * dt); + if (Type.SpeedMultiplier != 1) + Speed *= (float)Math.Pow(Type.SpeedMultiplier, dt); + + //Scale Out + if (Type.ScaleOut) + Size = StartSize * Ease.CubeOut(ease); + } + + public void Render() + { + var renderAt = new Vector2((int)Position.X, (int)Position.Y); + if (Track != null) + renderAt += Track.Position; + + Draw.SpriteBatch.Draw(Source.Texture, renderAt, Source.ClipRect, Color, Rotation, Source.Center, Size, SpriteEffects.None, 0); + } + + public void Render(float alpha) + { + var renderAt = new Vector2((int)Position.X, (int)Position.Y); + if (Track != null) + renderAt += Track.Position; + + Draw.SpriteBatch.Draw(Source.Texture, renderAt, Source.ClipRect, Color * alpha, Rotation, Source.Center, Size, SpriteEffects.None, 0); + } + } +} diff --git a/MonocleEngineDemo/Monocle/Particles/ParticleSystem.cs b/MonocleEngineDemo/Monocle/Particles/ParticleSystem.cs new file mode 100644 index 0000000..8c9a7de --- /dev/null +++ b/MonocleEngineDemo/Monocle/Particles/ParticleSystem.cs @@ -0,0 +1,134 @@ +using Microsoft.Xna.Framework; +using System; + +namespace Monocle +{ + public class ParticleSystem : Entity + { + private Particle[] particles; + private int nextSlot; + + public ParticleSystem(int depth, int maxParticles) + : base() + { + particles = new Particle[maxParticles]; + Depth = depth; + } + + public void Clear() + { + for (int i = 0; i < particles.Length; i++) + particles[i].Active = false; + } + + public void ClearRect(Rectangle rect, bool inside) + { + for (int i = 0; i < particles.Length; i ++) + { + var pos = particles[i].Position; + var isInside = (pos.X > rect.Left && pos.Y > rect.Top && pos.X < rect.Right && pos.Y < rect.Bottom); + + if (isInside == inside) + particles[i].Active = false; + } + } + + public override void Update() + { + for (int i = 0; i < particles.Length; i++) + if (particles[i].Active) + particles[i].Update(); + } + + public override void Render() + { + foreach (var p in particles) + if (p.Active) + p.Render(); + } + + public void Render(float alpha) + { + foreach (var p in particles) + if (p.Active) + p.Render(alpha); + } + + public void Simulate(float duration, float interval, Action emitter) + { + var delta = 0.016f; + for (float time = 0f; time < duration; time += delta) + { + if ((int)((time - delta) / interval) < (int)(time / interval)) + emitter(this); + + for (int i = 0; i < particles.Length; i++) + if (particles[i].Active) + particles[i].Update(delta); + } + } + + public void Add(Particle particle) + { + particles[nextSlot] = particle; + nextSlot = (nextSlot + 1) % particles.Length; + } + + public void Emit(ParticleType type, Vector2 position) + { + type.Create(ref particles[nextSlot], position); + nextSlot = (nextSlot + 1) % particles.Length; + } + + public void Emit(ParticleType type, Vector2 position, float direction) + { + type.Create(ref particles[nextSlot], position, direction); + nextSlot = (nextSlot + 1) % particles.Length; + } + + public void Emit(ParticleType type, Vector2 position, Color color) + { + type.Create(ref particles[nextSlot], position, color); + nextSlot = (nextSlot + 1) % particles.Length; + } + + public void Emit(ParticleType type, Vector2 position, Color color, float direction) + { + type.Create(ref particles[nextSlot], position, color, direction); + nextSlot = (nextSlot + 1) % particles.Length; + } + + public void Emit(ParticleType type, int amount, Vector2 position, Vector2 positionRange) + { + for (int i = 0; i < amount; i++) + Emit(type, Calc.Random.Range(position - positionRange, position + positionRange)); + } + + public void Emit(ParticleType type, int amount, Vector2 position, Vector2 positionRange, float direction) + { + for (int i = 0; i < amount; i++) + Emit(type, Calc.Random.Range(position - positionRange, position + positionRange), direction); + } + + public void Emit(ParticleType type, int amount, Vector2 position, Vector2 positionRange, Color color) + { + for (int i = 0; i < amount; i++) + Emit(type, Calc.Random.Range(position - positionRange, position + positionRange), color); + } + + public void Emit(ParticleType type, int amount, Vector2 position, Vector2 positionRange, Color color, float direction) + { + for (int i = 0; i < amount; i++) + Emit(type, Calc.Random.Range(position - positionRange, position + positionRange), color, direction); + } + + public void Emit(ParticleType type, Entity track, int amount, Vector2 position, Vector2 positionRange, float direction) + { + for (int i = 0; i < amount; i++) + { + type.Create(ref particles[nextSlot], track, Calc.Random.Range(position - positionRange, position + positionRange), direction, type.Color); + nextSlot = (nextSlot + 1) % particles.Length; + } + } + } +} diff --git a/MonocleEngineDemo/Monocle/Particles/ParticleType.cs b/MonocleEngineDemo/Monocle/Particles/ParticleType.cs new file mode 100644 index 0000000..5c19ba5 --- /dev/null +++ b/MonocleEngineDemo/Monocle/Particles/ParticleType.cs @@ -0,0 +1,159 @@ +using Microsoft.Xna.Framework; +using System; +using System.Collections.Generic; + +namespace Monocle +{ + public class ParticleType + { + public enum ColorModes { Static, Choose, Blink, Fade }; + public enum FadeModes { None, Linear, Late, InAndOut }; + public enum RotationModes { None, Random, SameAsDirection }; + + private static List AllTypes = new List(); + + public MTexture Source; + public Chooser SourceChooser; + public Color Color; + public Color Color2; + public ColorModes ColorMode; + public FadeModes FadeMode; + public float SpeedMin; + public float SpeedMax; + public float SpeedMultiplier; + public Vector2 Acceleration; + public float Friction; + public float Direction; + public float DirectionRange; + public float LifeMin; + public float LifeMax; + public float Size; + public float SizeRange; + public float SpinMin; + public float SpinMax; + public bool SpinFlippedChance; + public RotationModes RotationMode; + public bool ScaleOut; + public bool UseActualDeltaTime; + + public ParticleType() + { + Color = Color2 = Color.White; + ColorMode = ColorModes.Static; + FadeMode = FadeModes.None; + SpeedMin = SpeedMax = 0; + SpeedMultiplier = 1; + Acceleration = Vector2.Zero; + Friction = 0f; + Direction = DirectionRange = 0; + LifeMin = LifeMax = 0; + Size = 2; + SizeRange = 0; + SpinMin = SpinMax = 0; + SpinFlippedChance = false; + RotationMode = RotationModes.None; + + AllTypes.Add(this); + } + + public ParticleType(ParticleType copyFrom) + { + Source = copyFrom.Source; + SourceChooser = copyFrom.SourceChooser; + Color = copyFrom.Color; + Color2 = copyFrom.Color2; + ColorMode = copyFrom.ColorMode; + FadeMode = copyFrom.FadeMode; + SpeedMin = copyFrom.SpeedMin; + SpeedMax = copyFrom.SpeedMax; + SpeedMultiplier = copyFrom.SpeedMultiplier; + Acceleration = copyFrom.Acceleration; + Friction = copyFrom.Friction; + Direction = copyFrom.Direction; + DirectionRange = copyFrom.DirectionRange; + LifeMin = copyFrom.LifeMin; + LifeMax = copyFrom.LifeMax; + Size = copyFrom.Size; + SizeRange = copyFrom.SizeRange; + RotationMode = copyFrom.RotationMode; + SpinMin = copyFrom.SpinMin; + SpinMax = copyFrom.SpinMax; + SpinFlippedChance = copyFrom.SpinFlippedChance; + ScaleOut = copyFrom.ScaleOut; + UseActualDeltaTime = copyFrom.UseActualDeltaTime; + + AllTypes.Add(this); + } + + public Particle Create(ref Particle particle, Vector2 position) + { + return Create(ref particle, position, Direction); + } + + public Particle Create(ref Particle particle, Vector2 position, Color color) + { + return Create(ref particle, null, position, Direction, color); + } + + public Particle Create(ref Particle particle, Vector2 position, float direction) + { + return Create(ref particle, null, position, direction, Color); + } + + public Particle Create(ref Particle particle, Vector2 position, Color color, float direction) + { + return Create(ref particle, null, position, direction, color); + } + + public Particle Create(ref Particle particle, Entity entity, Vector2 position, float direction, Color color) + { + particle.Track = entity; + particle.Type = this; + particle.Active = true; + particle.Position = position; + + // source texture + if (SourceChooser != null) + particle.Source = SourceChooser.Choose(); + else if (Source != null) + particle.Source = Source; + else + particle.Source = Draw.Particle; + + // size + if (SizeRange != 0) + particle.StartSize = particle.Size = Size - SizeRange * .5f + Calc.Random.NextFloat(SizeRange); + else + particle.StartSize = particle.Size = Size; + + // color + if (ColorMode == ColorModes.Choose) + particle.StartColor = particle.Color = Calc.Random.Choose(color, Color2); + else + particle.StartColor = particle.Color = color; + + // speed / direction + var moveDirection = direction - DirectionRange / 2 + Calc.Random.NextFloat() * DirectionRange; + particle.Speed = Calc.AngleToVector(moveDirection, Calc.Random.Range(SpeedMin, SpeedMax)); + + // life + particle.StartLife = particle.Life = Calc.Random.Range(LifeMin, LifeMax); + + // rotation + if (RotationMode == RotationModes.Random) + particle.Rotation = Calc.Random.NextAngle(); + else if (RotationMode == RotationModes.SameAsDirection) + particle.Rotation = moveDirection; + else + particle.Rotation = 0; + + // spin + particle.Spin = Calc.Random.Range(SpinMin, SpinMax); + if (SpinFlippedChance) + particle.Spin *= Calc.Random.Choose(1, -1); + + return particle; + } + + } +} diff --git a/MonocleEngineDemo/Monocle/Properties/AssemblyInfo.cs b/MonocleEngineDemo/Monocle/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..0d4f5e7 --- /dev/null +++ b/MonocleEngineDemo/Monocle/Properties/AssemblyInfo.cs @@ -0,0 +1,33 @@ +using System.Reflection; +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("Monocle")] +[assembly: AssemblyProduct("Monocle")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyCompany("Matt Makes Games Inc.")] +[assembly: AssemblyCopyright("Copyright © Matt Makes Games Inc. 2016")] +[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. Only Windows +// assemblies support COM. +[assembly: ComVisible(false)] + +// On Windows, the following GUID is for the ID of the typelib if this +// project is exposed to COM. On other platforms, it unique identifies the +// title storage container when deploying this assembly to the device. +[assembly: Guid("9b1f2162-e5f3-44fa-8b15-ccae8caf333b")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +[assembly: AssemblyVersion("1.0.0.0")] diff --git a/MonocleEngineDemo/Monocle/Renderers/EverythingRenderer.cs b/MonocleEngineDemo/Monocle/Renderers/EverythingRenderer.cs new file mode 100644 index 0000000..56759c4 --- /dev/null +++ b/MonocleEngineDemo/Monocle/Renderers/EverythingRenderer.cs @@ -0,0 +1,43 @@ +using Microsoft.Xna.Framework.Graphics; + +namespace Monocle +{ + /// + /// Used for displaying data from current scene. + /// + public class EverythingRenderer : Renderer + { + public BlendState BlendState; + public SamplerState SamplerState; + public Effect Effect; + public Camera Camera; + + public EverythingRenderer() + { + BlendState = BlendState.AlphaBlend; + SamplerState = SamplerState.LinearClamp; + Camera = new Camera(); + } + + public override void BeforeRender(Scene scene) + { + + } + + public override void Render(Scene scene) + { + Draw.SpriteBatch.Begin(SpriteSortMode.Deferred, BlendState, SamplerState, DepthStencilState.None, RasterizerState.CullNone, Effect, Camera.Matrix * Engine.ScreenMatrix); + + scene.Entities.Render(); + if (Engine.Commands.Open) + scene.Entities.DebugRender(Camera); + + Draw.SpriteBatch.End(); + } + + public override void AfterRender(Scene scene) + { + + } + } +} diff --git a/MonocleEngineDemo/Monocle/Renderers/Renderer.cs b/MonocleEngineDemo/Monocle/Renderers/Renderer.cs new file mode 100644 index 0000000..7755f10 --- /dev/null +++ b/MonocleEngineDemo/Monocle/Renderers/Renderer.cs @@ -0,0 +1,12 @@ + +namespace Monocle +{ + public abstract class Renderer + { + public bool Visible = true; + public virtual void Update(Scene scene) { } + public virtual void BeforeRender(Scene scene) { } + public virtual void Render(Scene scene) { } + public virtual void AfterRender(Scene scene) { } + } +} diff --git a/MonocleEngineDemo/Monocle/Renderers/SingleTagRenderer.cs b/MonocleEngineDemo/Monocle/Renderers/SingleTagRenderer.cs new file mode 100644 index 0000000..1d2917f --- /dev/null +++ b/MonocleEngineDemo/Monocle/Renderers/SingleTagRenderer.cs @@ -0,0 +1,46 @@ +using Microsoft.Xna.Framework.Graphics; + +namespace Monocle +{ + public class SingleTagRenderer : Renderer + { + public BitTag Tag; + public BlendState BlendState; + public SamplerState SamplerState; + public Effect Effect; + public Camera Camera; + + public SingleTagRenderer(BitTag tag) + { + Tag = tag; + BlendState = BlendState.AlphaBlend; + SamplerState = SamplerState.LinearClamp; + Camera = new Camera(); + } + + public override void BeforeRender(Scene scene) + { + + } + + public override void Render(Scene scene) + { + Draw.SpriteBatch.Begin(SpriteSortMode.Deferred, BlendState, SamplerState, DepthStencilState.None, RasterizerState.CullNone, Effect, Camera.Matrix * Engine.ScreenMatrix); + + foreach (var entity in scene[Tag]) + if (entity.Visible) + entity.Render(); + + if (Engine.Commands.Open) + foreach (var entity in scene[Tag]) + entity.DebugRender(Camera); + + Draw.SpriteBatch.End(); + } + + public override void AfterRender(Scene scene) + { + + } + } +} diff --git a/MonocleEngineDemo/Monocle/Renderers/TagExcludeRenderer.cs b/MonocleEngineDemo/Monocle/Renderers/TagExcludeRenderer.cs new file mode 100644 index 0000000..4702e70 --- /dev/null +++ b/MonocleEngineDemo/Monocle/Renderers/TagExcludeRenderer.cs @@ -0,0 +1,51 @@ +using Microsoft.Xna.Framework.Graphics; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Monocle +{ + public class TagExcludeRenderer : Renderer + { + public BlendState BlendState; + public SamplerState SamplerState; + public Effect Effect; + public Camera Camera; + public int ExcludeTag; + + public TagExcludeRenderer(int excludeTag) + { + ExcludeTag = excludeTag; + BlendState = BlendState.AlphaBlend; + SamplerState = SamplerState.LinearClamp; + Camera = new Camera(); + } + + public override void BeforeRender(Scene scene) + { + + } + + public override void Render(Scene scene) + { + Draw.SpriteBatch.Begin(SpriteSortMode.Deferred, BlendState, SamplerState, DepthStencilState.None, RasterizerState.CullNone, Effect, Camera.Matrix * Engine.ScreenMatrix); + + foreach (var entity in scene.Entities) + if (entity.Visible && (entity.Tag & ExcludeTag) == 0) + entity.Render(); + + if (Engine.Commands.Open) + foreach (var entity in scene.Entities) + if ((entity.Tag & ExcludeTag) == 0) + entity.DebugRender(Camera); + + Draw.SpriteBatch.End(); + } + + public override void AfterRender(Scene scene) + { + + } + } +} diff --git a/MonocleEngineDemo/Monocle/Scene.cs b/MonocleEngineDemo/Monocle/Scene.cs new file mode 100644 index 0000000..804fe57 --- /dev/null +++ b/MonocleEngineDemo/Monocle/Scene.cs @@ -0,0 +1,861 @@ +using Microsoft.Xna.Framework; +using System; +using System.Collections; +using System.Collections.Generic; + +namespace Monocle +{ + public class Scene : IEnumerable, IEnumerable + { + public bool Paused; + public float TimeActive; + public float RawTimeActive; + public bool Focused { get; private set; } + public EntityList Entities { get; private set; } + public TagLists TagLists { get; private set; } + public RendererList RendererList { get; private set; } + public Entity HelperEntity { get; private set; } + public Tracker Tracker { get; private set; } + + private Dictionary actualDepthLookup; + + public event Action OnEndOfFrame; + + public Scene() + { + Tracker = new Tracker(); + Entities = new EntityList(this); + TagLists = new TagLists(); + RendererList = new RendererList(this); + + actualDepthLookup = new Dictionary(); + + HelperEntity = new Entity(); + Entities.Add(HelperEntity); + } + + public virtual void Begin() + { + Focused = true; + foreach (var entity in Entities) + entity.SceneBegin(this); + } + + public virtual void End() + { + Focused = false; + foreach (var entity in Entities) + entity.SceneEnd(this); + } + + public virtual void BeforeUpdate() + { + if (!Paused) + TimeActive += Engine.DeltaTime; + RawTimeActive += Engine.RawDeltaTime; + + Entities.UpdateLists(); + TagLists.UpdateLists(); + RendererList.UpdateLists(); + } + + public virtual void Update() + { + if (!Paused) + { + Entities.Update(); + RendererList.Update(); + } + } + + public virtual void AfterUpdate() + { + if (OnEndOfFrame != null) + { + OnEndOfFrame(); + OnEndOfFrame = null; + } + } + + public virtual void BeforeRender() + { + RendererList.BeforeRender(); + } + + public virtual void Render() + { + RendererList.Render(); + } + + public virtual void AfterRender() + { + RendererList.AfterRender(); + } + + public virtual void HandleGraphicsReset() + { + Entities.HandleGraphicsReset(); + } + + public virtual void HandleGraphicsCreate() + { + Entities.HandleGraphicsCreate(); + } + + public virtual void GainFocus() + { + + } + + public virtual void LoseFocus() + { + + } + + #region Interval + + /// + /// Returns whether the Scene timer has passed the given time interval since the last frame. Ex: given 2.0f, this will return true once every 2 seconds + /// + /// The time interval to check for + /// + public bool OnInterval(float interval) + { + return (int)((TimeActive - Engine.DeltaTime) / interval) < (int)(TimeActive / interval); + } + + /// + /// Returns whether the Scene timer has passed the given time interval since the last frame. Ex: given 2.0f, this will return true once every 2 seconds + /// + /// The time interval to check for + /// + public bool OnInterval(float interval, float offset) + { + return Math.Floor((TimeActive - offset - Engine.DeltaTime) / interval) < Math.Floor((TimeActive - offset) / interval); + } + + public bool BetweenInterval(float interval) + { + return Calc.BetweenInterval(TimeActive, interval); + } + + public bool OnRawInterval(float interval) + { + return (int)((RawTimeActive - Engine.RawDeltaTime) / interval) < (int)(RawTimeActive / interval); + } + + public bool OnRawInterval(float interval, float offset) + { + return Math.Floor((RawTimeActive - offset - Engine.RawDeltaTime) / interval) < Math.Floor((RawTimeActive - offset) / interval); + } + + public bool BetweenRawInterval(float interval) + { + return Calc.BetweenInterval(RawTimeActive, interval); + } + + #endregion + + #region Collisions v Tags + + public bool CollideCheck(Vector2 point, int tag) + { + var list = TagLists[(int)tag]; + + for (int i = 0; i < list.Count; i++) + if (list[i].Collidable && list[i].CollidePoint(point)) + return true; + return false; + } + + public bool CollideCheck(Vector2 from, Vector2 to, int tag) + { + var list = TagLists[(int)tag]; + + for (int i = 0; i < list.Count; i++) + if (list[i].Collidable && list[i].CollideLine(from, to)) + return true; + return false; + } + + public bool CollideCheck(Rectangle rect, int tag) + { + var list = TagLists[(int)tag]; + + for (int i = 0; i < list.Count; i++) + if (list[i].Collidable && list[i].CollideRect(rect)) + return true; + return false; + } + + public bool CollideCheck(Rectangle rect, Entity entity) + { + return (entity.Collidable && entity.CollideRect(rect)); + } + + public Entity CollideFirst(Vector2 point, int tag) + { + var list = TagLists[(int)tag]; + + for (int i = 0; i < list.Count; i++) + if (list[i].Collidable && list[i].CollidePoint(point)) + return list[i]; + return null; + } + + public Entity CollideFirst(Vector2 from, Vector2 to, int tag) + { + var list = TagLists[(int)tag]; + + for (int i = 0; i < list.Count; i++) + if (list[i].Collidable && list[i].CollideLine(from, to)) + return list[i]; + return null; + } + + public Entity CollideFirst(Rectangle rect, int tag) + { + var list = TagLists[(int)tag]; + + for (int i = 0; i < list.Count; i++) + if (list[i].Collidable && list[i].CollideRect(rect)) + return list[i]; + return null; + } + + public void CollideInto(Vector2 point, int tag, List hits) + { + var list = TagLists[(int)tag]; + + for (int i = 0; i < list.Count; i++) + if (list[i].Collidable && list[i].CollidePoint(point)) + hits.Add(list[i]); + } + + public void CollideInto(Vector2 from, Vector2 to, int tag, List hits) + { + var list = TagLists[(int)tag]; + + for (int i = 0; i < list.Count; i++) + if (list[i].Collidable && list[i].CollideLine(from, to)) + hits.Add(list[i]); + } + + public void CollideInto(Rectangle rect, int tag, List hits) + { + var list = TagLists[(int)tag]; + + for (int i = 0; i < list.Count; i++) + if (list[i].Collidable && list[i].CollideRect(rect)) + list.Add(list[i]); + } + + public List CollideAll(Vector2 point, int tag) + { + List list = new List(); + CollideInto(point, tag, list); + return list; + } + + public List CollideAll(Vector2 from, Vector2 to, int tag) + { + List list = new List(); + CollideInto(from, to, tag, list); + return list; + } + + public List CollideAll(Rectangle rect, int tag) + { + List list = new List(); + CollideInto(rect, tag, list); + return list; + } + + public void CollideDo(Vector2 point, int tag, Action action) + { + var list = TagLists[(int)tag]; + + for (int i = 0; i < list.Count; i++) + if (list[i].Collidable && list[i].CollidePoint(point)) + action(list[i]); + } + + public void CollideDo(Vector2 from, Vector2 to, int tag, Action action) + { + var list = TagLists[(int)tag]; + + for (int i = 0; i < list.Count; i++) + if (list[i].Collidable && list[i].CollideLine(from, to)) + action(list[i]); + } + + public void CollideDo(Rectangle rect, int tag, Action action) + { + var list = TagLists[(int)tag]; + + for (int i = 0; i < list.Count; i++) + if (list[i].Collidable && list[i].CollideRect(rect)) + action(list[i]); + } + + public Vector2 LineWalkCheck(Vector2 from, Vector2 to, int tag, float precision) + { + Vector2 add = to - from; + add.Normalize(); + add *= precision; + + int amount = (int)Math.Floor((from - to).Length() / precision); + Vector2 prev = from; + Vector2 at = from + add; + + for (int i = 0; i <= amount; i++) + { + if (CollideCheck(at, tag)) + return prev; + prev = at; + at += add; + } + + return to; + } + + #endregion + + #region Collisions v Tracked List Entities + + public bool CollideCheck(Vector2 point) where T : Entity + { + var list = Tracker.Entities[typeof(T)]; + + for (int i = 0; i < list.Count; i++) + if (list[i].Collidable && list[i].CollidePoint(point)) + return true; + return false; + } + + public bool CollideCheck(Vector2 from, Vector2 to) where T : Entity + { + var list = Tracker.Entities[typeof(T)]; + + for (int i = 0; i < list.Count; i++) + if (list[i].Collidable && list[i].CollideLine(from, to)) + return true; + return false; + } + + public bool CollideCheck(Rectangle rect) where T : Entity + { + var list = Tracker.Entities[typeof(T)]; + + for (int i = 0; i < list.Count; i++) + if (list[i].Collidable && list[i].CollideRect(rect)) + return true; + return false; + } + + public T CollideFirst(Vector2 point) where T : Entity + { + var list = Tracker.Entities[typeof(T)]; + + for (int i = 0; i < list.Count; i++) + if (list[i].Collidable && list[i].CollidePoint(point)) + return list[i] as T; + return null; + } + + public T CollideFirst(Vector2 from, Vector2 to) where T : Entity + { + var list = Tracker.Entities[typeof(T)]; + + for (int i = 0; i < list.Count; i++) + if (list[i].Collidable && list[i].CollideLine(from, to)) + return list[i] as T; + return null; + } + + public T CollideFirst(Rectangle rect) where T : Entity + { + var list = Tracker.Entities[typeof(T)]; + + for (int i = 0; i < list.Count; i++) + if (list[i].Collidable && list[i].CollideRect(rect)) + return list[i] as T; + return null; + } + + public void CollideInto(Vector2 point, List hits) where T : Entity + { + var list = Tracker.Entities[typeof(T)]; + + for (int i = 0; i < list.Count; i++) + if (list[i].Collidable && list[i].CollidePoint(point)) + hits.Add(list[i]); + } + + public void CollideInto(Vector2 from, Vector2 to, List hits) where T : Entity + { + var list = Tracker.Entities[typeof(T)]; + + for (int i = 0; i < list.Count; i++) + if (list[i].Collidable && list[i].CollideLine(from, to)) + hits.Add(list[i]); + } + + public void CollideInto(Rectangle rect, List hits) where T : Entity + { + var list = Tracker.Entities[typeof(T)]; + + for (int i = 0; i < list.Count; i++) + if (list[i].Collidable && list[i].CollideRect(rect)) + list.Add(list[i]); + } + + public void CollideInto(Vector2 point, List hits) where T : Entity + { + var list = Tracker.Entities[typeof(T)]; + + for (int i = 0; i < list.Count; i++) + if (list[i].Collidable && list[i].CollidePoint(point)) + hits.Add(list[i] as T); + } + + public void CollideInto(Vector2 from, Vector2 to, List hits) where T : Entity + { + var list = Tracker.Entities[typeof(T)]; + + for (int i = 0; i < list.Count; i++) + if (list[i].Collidable && list[i].CollideLine(from, to)) + hits.Add(list[i] as T); + } + + public void CollideInto(Rectangle rect, List hits) where T : Entity + { + var list = Tracker.Entities[typeof(T)]; + + for (int i = 0; i < list.Count; i++) + if (list[i].Collidable && list[i].CollideRect(rect)) + hits.Add(list[i] as T); + } + + public List CollideAll(Vector2 point) where T : Entity + { + List list = new List(); + CollideInto(point, list); + return list; + } + + public List CollideAll(Vector2 from, Vector2 to) where T : Entity + { + List list = new List(); + CollideInto(from, to, list); + return list; + } + + public List CollideAll(Rectangle rect) where T : Entity + { + List list = new List(); + CollideInto(rect, list); + return list; + } + + public void CollideDo(Vector2 point, Action action) where T : Entity + { + var list = Tracker.Entities[typeof(T)]; + + for (int i = 0; i < list.Count; i++) + if (list[i].Collidable && list[i].CollidePoint(point)) + action(list[i] as T); + } + + public void CollideDo(Vector2 from, Vector2 to, Action action) where T : Entity + { + var list = Tracker.Entities[typeof(T)]; + + for (int i = 0; i < list.Count; i++) + if (list[i].Collidable && list[i].CollideLine(from, to)) + action(list[i] as T); + } + + public void CollideDo(Rectangle rect, Action action) where T : Entity + { + var list = Tracker.Entities[typeof(T)]; + + for (int i = 0; i < list.Count; i++) + if (list[i].Collidable && list[i].CollideRect(rect)) + action(list[i] as T); + } + + public Vector2 LineWalkCheck(Vector2 from, Vector2 to, float precision) where T : Entity + { + Vector2 add = to - from; + add.Normalize(); + add *= precision; + + int amount = (int)Math.Floor((from - to).Length() / precision); + Vector2 prev = from; + Vector2 at = from + add; + + for (int i = 0; i <= amount; i++) + { + if (CollideCheck(at)) + return prev; + prev = at; + at += add; + } + + return to; + } + + #endregion + + #region Collisions v Tracked List Components + + public bool CollideCheckByComponent(Vector2 point) where T : Component + { + var list = Tracker.Components[typeof(T)]; + + for (int i = 0; i < list.Count; i++) + if (list[i].Entity.Collidable && list[i].Entity.CollidePoint(point)) + return true; + return false; + } + + public bool CollideCheckByComponent(Vector2 from, Vector2 to) where T : Component + { + var list = Tracker.Components[typeof(T)]; + + for (int i = 0; i < list.Count; i++) + if (list[i].Entity.Collidable && list[i].Entity.CollideLine(from, to)) + return true; + return false; + } + + public bool CollideCheckByComponent(Rectangle rect) where T : Component + { + var list = Tracker.Components[typeof(T)]; + + for (int i = 0; i < list.Count; i++) + if (list[i].Entity.Collidable && list[i].Entity.CollideRect(rect)) + return true; + return false; + } + + public T CollideFirstByComponent(Vector2 point) where T : Component + { + var list = Tracker.Components[typeof(T)]; + + for (int i = 0; i < list.Count; i++) + if (list[i].Entity.Collidable && list[i].Entity.CollidePoint(point)) + return list[i] as T; + return null; + } + + public T CollideFirstByComponent(Vector2 from, Vector2 to) where T : Component + { + var list = Tracker.Components[typeof(T)]; + + for (int i = 0; i < list.Count; i++) + if (list[i].Entity.Collidable && list[i].Entity.CollideLine(from, to)) + return list[i] as T; + return null; + } + + public T CollideFirstByComponent(Rectangle rect) where T : Component + { + var list = Tracker.Components[typeof(T)]; + + for (int i = 0; i < list.Count; i++) + if (list[i].Entity.Collidable && list[i].Entity.CollideRect(rect)) + return list[i] as T; + return null; + } + + public void CollideIntoByComponent(Vector2 point, List hits) where T : Component + { + var list = Tracker.Components[typeof(T)]; + + for (int i = 0; i < list.Count; i++) + if (list[i].Entity.Collidable && list[i].Entity.CollidePoint(point)) + hits.Add(list[i]); + } + + public void CollideIntoByComponent(Vector2 from, Vector2 to, List hits) where T : Component + { + var list = Tracker.Components[typeof(T)]; + + for (int i = 0; i < list.Count; i++) + if (list[i].Entity.Collidable && list[i].Entity.CollideLine(from, to)) + hits.Add(list[i]); + } + + public void CollideIntoByComponent(Rectangle rect, List hits) where T : Component + { + var list = Tracker.Components[typeof(T)]; + + for (int i = 0; i < list.Count; i++) + if (list[i].Entity.Collidable && list[i].Entity.CollideRect(rect)) + list.Add(list[i]); + } + + public void CollideIntoByComponent(Vector2 point, List hits) where T : Component + { + var list = Tracker.Components[typeof(T)]; + + for (int i = 0; i < list.Count; i++) + if (list[i].Entity.Collidable && list[i].Entity.CollidePoint(point)) + hits.Add(list[i] as T); + } + + public void CollideIntoByComponent(Vector2 from, Vector2 to, List hits) where T : Component + { + var list = Tracker.Components[typeof(T)]; + + for (int i = 0; i < list.Count; i++) + if (list[i].Entity.Collidable && list[i].Entity.CollideLine(from, to)) + hits.Add(list[i] as T); + } + + public void CollideIntoByComponent(Rectangle rect, List hits) where T : Component + { + var list = Tracker.Components[typeof(T)]; + + for (int i = 0; i < list.Count; i++) + if (list[i].Entity.Collidable && list[i].Entity.CollideRect(rect)) + list.Add(list[i] as T); + } + + public List CollideAllByComponent(Vector2 point) where T : Component + { + List list = new List(); + CollideIntoByComponent(point, list); + return list; + } + + public List CollideAllByComponent(Vector2 from, Vector2 to) where T : Component + { + List list = new List(); + CollideIntoByComponent(from, to, list); + return list; + } + + public List CollideAllByComponent(Rectangle rect) where T : Component + { + List list = new List(); + CollideIntoByComponent(rect, list); + return list; + } + + public void CollideDoByComponent(Vector2 point, Action action) where T : Component + { + var list = Tracker.Components[typeof(T)]; + + for (int i = 0; i < list.Count; i++) + if (list[i].Entity.Collidable && list[i].Entity.CollidePoint(point)) + action(list[i] as T); + } + + public void CollideDoByComponent(Vector2 from, Vector2 to, Action action) where T : Component + { + var list = Tracker.Components[typeof(T)]; + + for (int i = 0; i < list.Count; i++) + if (list[i].Entity.Collidable && list[i].Entity.CollideLine(from, to)) + action(list[i] as T); + } + + public void CollideDoByComponent(Rectangle rect, Action action) where T : Component + { + var list = Tracker.Components[typeof(T)]; + + for (int i = 0; i < list.Count; i++) + if (list[i].Entity.Collidable && list[i].Entity.CollideRect(rect)) + action(list[i] as T); + } + + public Vector2 LineWalkCheckByComponent(Vector2 from, Vector2 to, float precision) where T : Component + { + Vector2 add = to - from; + add.Normalize(); + add *= precision; + + int amount = (int)Math.Floor((from - to).Length() / precision); + Vector2 prev = from; + Vector2 at = from + add; + + for (int i = 0; i <= amount; i++) + { + if (CollideCheckByComponent(at)) + return prev; + prev = at; + at += add; + } + + return to; + } + + #endregion + + #region Utils + + internal void SetActualDepth(Entity entity) + { + const double theta = .000001f; + + double add = 0; + if (actualDepthLookup.TryGetValue(entity.depth, out add)) + actualDepthLookup[entity.depth] += theta; + else + actualDepthLookup.Add(entity.depth, theta); + entity.actualDepth = entity.depth - add; + + //Mark lists unsorted + Entities.MarkUnsorted(); + for (int i = 0; i < BitTag.TotalTags; i++) + if (entity.TagCheck(1 << i)) + TagLists.MarkUnsorted(i); + } + + #endregion + + #region Entity Shortcuts + + /// + /// Shortcut to call Engine.Pooler.Create, add the Entity to this Scene, and return it. Entity type must be marked as Pooled + /// + /// Pooled Entity type to create + /// + public T CreateAndAdd() where T : Entity, new() + { + var entity = Engine.Pooler.Create(); + Add(entity); + return entity; + } + + /// + /// Quick access to entire tag lists of Entities. Result will never be null + /// + /// The tag list to fetch + /// + public List this[BitTag tag] + { + get + { + return TagLists[tag.ID]; + } + } + + /// + /// Shortcut function for adding an Entity to the Scene's Entities list + /// + /// The Entity to add + public void Add(Entity entity) + { + Entities.Add(entity); + } + + /// + /// Shortcut function for removing an Entity from the Scene's Entities list + /// + /// The Entity to remove + public void Remove(Entity entity) + { + Entities.Remove(entity); + } + + /// + /// Shortcut function for adding a set of Entities from the Scene's Entities list + /// + /// The Entities to add + public void Add(IEnumerable entities) + { + Entities.Add(entities); + } + + /// + /// Shortcut function for removing a set of Entities from the Scene's Entities list + /// + /// The Entities to remove + public void Remove(IEnumerable entities) + { + Entities.Remove(entities); + } + + /// + /// Shortcut function for adding a set of Entities from the Scene's Entities list + /// + /// The Entities to add + public void Add(params Entity[] entities) + { + Entities.Add(entities); + } + + /// + /// Shortcut function for removing a set of Entities from the Scene's Entities list + /// + /// The Entities to remove + public void Remove(params Entity[] entities) + { + Entities.Remove(entities); + } + + /// + /// Allows you to iterate through all Entities in the Scene + /// + /// + public IEnumerator GetEnumerator() + { + return Entities.GetEnumerator(); + } + + /// + /// Allows you to iterate through all Entities in the Scene + /// + /// + IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public List GetEntitiesByTagMask(int mask) + { + List list = new List(); + foreach (var entity in Entities) + if ((entity.Tag & mask) != 0) + list.Add(entity); + return list; + } + + public List GetEntitiesExcludingTagMask(int mask) + { + List list = new List(); + foreach (var entity in Entities) + if ((entity.Tag & mask) == 0) + list.Add(entity); + return list; + } + + #endregion + + #region Renderer Shortcuts + + /// + /// Shortcut function to add a Renderer to the Renderer list + /// + /// The Renderer to add + public void Add(Renderer renderer) + { + RendererList.Add(renderer); + } + + /// + /// Shortcut function to remove a Renderer from the Renderer list + /// + /// The Renderer to remove + public void Remove(Renderer renderer) + { + RendererList.Remove(renderer); + } + + #endregion + } +} diff --git a/MonocleEngineDemo/Monocle/Util/BitTag.cs b/MonocleEngineDemo/Monocle/Util/BitTag.cs new file mode 100644 index 0000000..f60de45 --- /dev/null +++ b/MonocleEngineDemo/Monocle/Util/BitTag.cs @@ -0,0 +1,55 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Monocle +{ + /// + /// Used mainly for categorizing types of objects (especially for collisions). You can create up to 32 different tags! + /// + public class BitTag + { + internal static int TotalTags = 0; + internal static BitTag[] byID = new BitTag[32]; + private static Dictionary byName = new Dictionary(StringComparer.OrdinalIgnoreCase); + + public static BitTag Get(string name) + { +#if DEBUG + if (!byName.ContainsKey(name)) + throw new Exception("No tag with the name '" + name + "' has been defined!"); +#endif + return byName[name]; + } + + public int ID; + public int Value; + public string Name; + + public BitTag(string name) + { +#if DEBUG + if (TotalTags >= 32) + throw new Exception("Maximum tag limit of 32 exceeded!"); + if (byName.ContainsKey(name)) + throw new Exception("Two tags defined with the same name: '" + name + "'!"); +#endif + + ID = TotalTags; + Value = 1 << TotalTags; + Name = name; + + byID[ID] = this; + byName[name] = this; + + TotalTags++; + } + + public static implicit operator int(BitTag tag) + { + return tag.Value; + } + } +} diff --git a/MonocleEngineDemo/Monocle/Util/Cache.cs b/MonocleEngineDemo/Monocle/Util/Cache.cs new file mode 100644 index 0000000..7051982 --- /dev/null +++ b/MonocleEngineDemo/Monocle/Util/Cache.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; + +namespace Monocle +{ + public static class Cache + { + public static Dictionary> cache; + + private static void Init() where T : Entity, new() + { + if (cache == null) + cache = new Dictionary>(); + if (!cache.ContainsKey(typeof(T))) + cache.Add(typeof(T), new Stack()); + } + + public static void Store(T instance) where T : Entity, new() + { + Init(); + cache[typeof(T)].Push(instance); + } + + public static T Create() where T : Entity, new() + { + Init(); + if (cache[typeof(T)].Count > 0) + return cache[typeof(T)].Pop() as T; + else + return new T(); + } + + public static void Clear() where T : Entity, new() + { + if (cache != null && cache.ContainsKey(typeof(T))) + cache[typeof(T)].Clear(); + } + + public static void ClearAll() + { + if (cache != null) + foreach (var kv in cache) + kv.Value.Clear(); + } + } +} diff --git a/MonocleEngineDemo/Monocle/Util/Calc.cs b/MonocleEngineDemo/Monocle/Util/Calc.cs new file mode 100644 index 0000000..f7f614b --- /dev/null +++ b/MonocleEngineDemo/Monocle/Util/Calc.cs @@ -0,0 +1,2198 @@ +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using System; +using System.Collections.Generic; +using System.Collections; +using System.Diagnostics; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Xml; +using System.Xml.Serialization; + +namespace Monocle +{ + public static class Calc + { + #region Enums + + public static int EnumLength(Type e) + { + return Enum.GetNames(e).Length; + } + + public static T StringToEnum(string str) where T : struct + { + if (Enum.IsDefined(typeof(T), str)) + return (T)Enum.Parse(typeof(T), str); + else + throw new Exception("The string cannot be converted to the enum type."); + } + + public static T[] StringsToEnums(string[] strs) where T : struct + { + T[] ret = new T[strs.Length]; + for (int i = 0; i < strs.Length; i++) + ret[i] = StringToEnum(strs[i]); + return ret; + } + + public static bool EnumHasString(string str) where T : struct + { + return Enum.IsDefined(typeof(T), str); + } + + #endregion + + #region Strings + + public static bool StartsWith(this string str, string match) + { + return str.IndexOf(match) == 0; + } + + public static bool EndsWith(this string str, string match) + { + return str.LastIndexOf(match) == str.Length - match.Length; + } + + public static bool IsIgnoreCase(this string str, params string[] matches) + { + if (string.IsNullOrEmpty(str)) + return false; + + foreach (var match in matches) + if (str.Equals(match, StringComparison.InvariantCultureIgnoreCase)) + return true; + + return false; + } + + public static string ToString(this int num, int minDigits) + { + string ret = num.ToString(); + while (ret.Length < minDigits) + ret = "0" + ret; + return ret; + } + + public static string[] SplitLines(string text, SpriteFont font, int maxLineWidth, char newLine = '\n') + { + List lines = new List(); + + foreach (var forcedLine in text.Split(newLine)) + { + string line = ""; + + foreach (string word in forcedLine.Split(' ')) + { + if (font.MeasureString(line + " " + word).X > maxLineWidth) + { + lines.Add(line); + line = word; + } + else + { + if (line != "") + line += " "; + line += word; + } + } + + lines.Add(line); + } + + return lines.ToArray(); + } + + #endregion + + #region Count + + public static int Count(T target, T a, T b) + { + int num = 0; + + if (a.Equals(target)) + num++; + if (b.Equals(target)) + num++; + + return num; + } + + public static int Count(T target, T a, T b, T c) + { + int num = 0; + + if (a.Equals(target)) + num++; + if (b.Equals(target)) + num++; + if (c.Equals(target)) + num++; + + return num; + } + + public static int Count(T target, T a, T b, T c, T d) + { + int num = 0; + + if (a.Equals(target)) + num++; + if (b.Equals(target)) + num++; + if (c.Equals(target)) + num++; + if (d.Equals(target)) + num++; + + return num; + } + + public static int Count(T target, T a, T b, T c, T d, T e) + { + int num = 0; + + if (a.Equals(target)) + num++; + if (b.Equals(target)) + num++; + if (c.Equals(target)) + num++; + if (d.Equals(target)) + num++; + if (e.Equals(target)) + num++; + + return num; + } + + public static int Count(T target, T a, T b, T c, T d, T e, T f) + { + int num = 0; + + if (a.Equals(target)) + num++; + if (b.Equals(target)) + num++; + if (c.Equals(target)) + num++; + if (d.Equals(target)) + num++; + if (e.Equals(target)) + num++; + if (f.Equals(target)) + num++; + + return num; + } + + #endregion + + #region Give Me + + public static T GiveMe(int index, T a, T b) + { + switch (index) + { + default: + throw new Exception("Index was out of range!"); + + case 0: + return a; + case 1: + return b; + } + } + + public static T GiveMe(int index, T a, T b, T c) + { + switch (index) + { + default: + throw new Exception("Index was out of range!"); + + case 0: + return a; + case 1: + return b; + case 2: + return c; + } + } + + public static T GiveMe(int index, T a, T b, T c, T d) + { + switch (index) + { + default: + throw new Exception("Index was out of range!"); + + case 0: + return a; + case 1: + return b; + case 2: + return c; + case 3: + return d; + } + } + + public static T GiveMe(int index, T a, T b, T c, T d, T e) + { + switch (index) + { + default: + throw new Exception("Index was out of range!"); + + case 0: + return a; + case 1: + return b; + case 2: + return c; + case 3: + return d; + case 4: + return e; + } + } + + public static T GiveMe(int index, T a, T b, T c, T d, T e, T f) + { + switch (index) + { + default: + throw new Exception("Index was out of range!"); + + case 0: + return a; + case 1: + return b; + case 2: + return c; + case 3: + return d; + case 4: + return e; + case 5: + return f; + } + } + + #endregion + + #region Random + + public static Random Random = new Random(); + private static Stack randomStack = new Stack(); + + public static void PushRandom(int newSeed) + { + randomStack.Push(Calc.Random); + Calc.Random = new Random(newSeed); + } + + public static void PushRandom(Random random) + { + randomStack.Push(Calc.Random); + Calc.Random = random; + } + + public static void PushRandom() + { + randomStack.Push(Calc.Random); + Calc.Random = new Random(); + } + + public static void PopRandom() + { + Calc.Random = randomStack.Pop(); + } + + #region Choose + + public static T Choose(this Random random, T a, T b) + { + return GiveMe(random.Next(2), a, b); + } + + public static T Choose(this Random random, T a, T b, T c) + { + return GiveMe(random.Next(3), a, b, c); + } + + public static T Choose(this Random random, T a, T b, T c, T d) + { + return GiveMe(random.Next(4), a, b, c, d); + } + + public static T Choose(this Random random, T a, T b, T c, T d, T e) + { + return GiveMe(random.Next(5), a, b, c, d, e); + } + + public static T Choose(this Random random, T a, T b, T c, T d, T e, T f) + { + return GiveMe(random.Next(6), a, b, c, d, e, f); + } + + public static T Choose(this Random random, params T[] choices) + { + return choices[random.Next(choices.Length)]; + } + + public static T Choose(this Random random, List choices) + { + return choices[random.Next(choices.Count)]; + } + + #endregion + + #region Range + + /// + /// Returns a random integer between min (inclusive) and max (exclusive) + /// + /// + /// + /// + /// + public static int Range(this Random random, int min, int max) + { + return min + random.Next(max - min); + } + + /// + /// Returns a random float between min (inclusive) and max (exclusive) + /// + /// + /// + /// + /// + public static float Range(this Random random, float min, float max) + { + return min + random.NextFloat(max - min); + } + + /// + /// Returns a random Vector2, and x- and y-values of which are between min (inclusive) and max (exclusive) + /// + /// + /// + /// + /// + public static Vector2 Range(this Random random, Vector2 min, Vector2 max) + { + return min + new Vector2(random.NextFloat(max.X - min.X), random.NextFloat(max.Y - min.Y)); + } + + #endregion + + public static int Facing(this Random random) + { + return (random.NextFloat() < 0.5f ? -1 : 1); + } + + public static bool Chance(this Random random, float chance) + { + return random.NextFloat() < chance; + } + + public static float NextFloat(this Random random) + { + return (float)random.NextDouble(); + } + + public static float NextFloat(this Random random, float max) + { + return random.NextFloat() * max; + } + + public static float NextAngle(this Random random) + { + return random.NextFloat() * MathHelper.TwoPi; + } + + private static int[] shakeVectorOffsets = new int[] { -1, -1, 0, 1, 1 }; + + public static Vector2 ShakeVector(this Random random) + { + return new Vector2(random.Choose(shakeVectorOffsets), random.Choose(shakeVectorOffsets)); + } + + #endregion + + #region Lists + + public static Vector2 ClosestTo(this List list, Vector2 to) + { + Vector2 best = list[0]; + float distSq = Vector2.DistanceSquared(list[0], to); + + for (int i = 1; i < list.Count; i++) + { + float d = Vector2.DistanceSquared(list[i], to); + if (d < distSq) + { + distSq = d; + best = list[i]; + } + } + + return best; + } + + public static Vector2 ClosestTo(this Vector2[] list, Vector2 to) + { + Vector2 best = list[0]; + float distSq = Vector2.DistanceSquared(list[0], to); + + for (int i = 1; i < list.Length; i++) + { + float d = Vector2.DistanceSquared(list[i], to); + if (d < distSq) + { + distSq = d; + best = list[i]; + } + } + + return best; + } + + public static Vector2 ClosestTo(this Vector2[] list, Vector2 to, out int index) + { + index = 0; + Vector2 best = list[0]; + float distSq = Vector2.DistanceSquared(list[0], to); + + for (int i = 1; i < list.Length; i++) + { + float d = Vector2.DistanceSquared(list[i], to); + if (d < distSq) + { + index = i; + distSq = d; + best = list[i]; + } + } + + return best; + } + + public static void Shuffle(this List list, Random random) + { + int i = list.Count; + int j; + T t; + + while (--i > 0) + { + t = list[i]; + list[i] = list[j = random.Next(i + 1)]; + list[j] = t; + } + } + + public static void Shuffle(this List list) + { + list.Shuffle(Random); + } + + public static void ShuffleSetFirst(this List list, Random random, T first) + { + int amount = 0; + while (list.Contains(first)) + { + list.Remove(first); + amount++; + } + + list.Shuffle(random); + + for (int i = 0; i < amount; i++) + list.Insert(0, first); + } + + public static void ShuffleSetFirst(this List list, T first) + { + list.ShuffleSetFirst(Random, first); + } + + public static void ShuffleNotFirst(this List list, Random random, T notFirst) + { + int amount = 0; + while (list.Contains(notFirst)) + { + list.Remove(notFirst); + amount++; + } + + list.Shuffle(random); + + for (int i = 0; i < amount; i++) + list.Insert(random.Next(list.Count - 1) + 1, notFirst); + } + + public static void ShuffleNotFirst(this List list, T notFirst) + { + list.ShuffleNotFirst(Random, notFirst); + } + + #endregion + + #region Colors + + public static Color Invert(this Color color) + { + return new Color(255 - color.R, 255 - color.G, 255 - color.B, color.A); + } + + public static Color HexToColor(string hex) + { + if (hex.Length >= 6) + { + float r = (HexToByte(hex[0]) * 16 + HexToByte(hex[1])) / 255.0f; + float g = (HexToByte(hex[2]) * 16 + HexToByte(hex[3])) / 255.0f; + float b = (HexToByte(hex[4]) * 16 + HexToByte(hex[5])) / 255.0f; + return new Color(r, g, b); + } + + return Color.White; + } + + #endregion + + #region Time + + public static string ShortGameplayFormat(this TimeSpan time) + { + if (time.TotalHours >= 1) + return ((int)time.Hours) + ":" + time.ToString(@"mm\:ss\.fff"); + else + return time.ToString(@"m\:ss\.fff"); + } + + public static string LongGameplayFormat(this TimeSpan time) + { + StringBuilder str = new StringBuilder(); + + if (time.TotalDays >= 2) + { + str.Append((int)time.TotalDays); + str.Append(" days, "); + } + else if (time.TotalDays >= 1) + str.Append("1 day, "); + + str.Append((time.TotalHours - ((int)time.TotalDays * 24)).ToString("0.0")); + str.Append(" hours"); + + return str.ToString(); + } + + #endregion + + #region Math + + public const float Right = 0; + public const float Up = -MathHelper.PiOver2; + public const float Left = MathHelper.Pi; + public const float Down = MathHelper.PiOver2; + public const float UpRight = -MathHelper.PiOver4; + public const float UpLeft = -MathHelper.PiOver4 - MathHelper.PiOver2; + public const float DownRight = MathHelper.PiOver4; + public const float DownLeft = MathHelper.PiOver4 + MathHelper.PiOver2; + public const float DegToRad = MathHelper.Pi / 180f; + public const float RadToDeg = 180f / MathHelper.Pi; + public const float DtR = DegToRad; + public const float RtD = RadToDeg; + public const float Circle = MathHelper.TwoPi; + public const float HalfCircle = MathHelper.Pi; + public const float QuarterCircle = MathHelper.PiOver2; + public const float EighthCircle = MathHelper.PiOver4; + private const string Hex = "0123456789ABCDEF"; + + public static int Digits(this int num) + { + int digits = 1; + int target = 10; + + while (num >= target) + { + digits++; + target *= 10; + } + + return digits; + } + + public static byte HexToByte(char c) + { + return (byte)Hex.IndexOf(char.ToUpper(c)); + } + + public static float Percent(float num, float zeroAt, float oneAt) + { + return MathHelper.Clamp((num - zeroAt) / oneAt, 0, 1); + } + + public static float SignThreshold(float value, float threshold) + { + if (Math.Abs(value) >= threshold) + return Math.Sign(value); + else + return 0; + } + + public static float Min(params float[] values) + { + float min = values[0]; + for (int i = 1; i < values.Length; i++) + min = MathHelper.Min(values[i], min); + return min; + } + + public static float Max(params float[] values) + { + float max = values[0]; + for (int i = 1; i < values.Length; i++) + max = MathHelper.Max(values[i], max); + return max; + } + + public static float ToRad(this float f) + { + return f * DegToRad; + } + + public static float ToDeg(this float f) + { + return f * RadToDeg; + } + + public static int Axis(bool negative, bool positive, int both = 0) + { + if (negative) + { + if (positive) + return both; + else + return -1; + } + else if (positive) + return 1; + else + return 0; + } + + public static int Clamp(int value, int min, int max) + { + return Math.Min(Math.Max(value, min), max); + } + + public static float Clamp(float value, float min, float max) + { + return Math.Min(Math.Max(value, min), max); + } + + public static float YoYo(float value) + { + if (value <= .5f) + return value * 2; + else + return 1 - ((value - .5f) * 2); + } + + public static float Map(float val, float min, float max, float newMin = 0, float newMax = 1) + { + return ((val - min) / (max - min)) * (newMax - newMin) + newMin; + } + + public static float SineMap(float counter, float newMin, float newMax) + { + return Calc.Map((float)Math.Sin(counter), 01, 1, newMin, newMax); + } + + public static float ClampedMap(float val, float min, float max, float newMin = 0, float newMax = 1) + { + return MathHelper.Clamp((val - min) / (max - min), 0, 1) * (newMax - newMin) + newMin; + } + + public static float LerpSnap(float value1, float value2, float amount, float snapThreshold = .1f) + { + float ret = MathHelper.Lerp(value1, value2, amount); + if (Math.Abs(ret - value2) < snapThreshold) + return value2; + else + return ret; + } + + public static float LerpClamp(float value1, float value2, float lerp) + { + return MathHelper.Lerp(value1, value2, MathHelper.Clamp(lerp, 0, 1)); + } + + public static Vector2 LerpSnap(Vector2 value1, Vector2 value2, float amount, float snapThresholdSq = .1f) + { + Vector2 ret = Vector2.Lerp(value1, value2, amount); + if ((ret - value2).LengthSquared() < snapThresholdSq) + return value2; + else + return ret; + } + + + public static Vector2 Sign(this Vector2 vec) + { + return new Vector2(Math.Sign(vec.X), Math.Sign(vec.Y)); + } + + public static Vector2 SafeNormalize(this Vector2 vec) + { + return SafeNormalize(vec, Vector2.Zero); + } + + public static Vector2 SafeNormalize(this Vector2 vec, float length) + { + return SafeNormalize(vec, Vector2.Zero, length); + } + + public static Vector2 SafeNormalize(this Vector2 vec, Vector2 ifZero) + { + if (vec == Vector2.Zero) + return ifZero; + else + { + vec.Normalize(); + return vec; + } + } + + public static Vector2 SafeNormalize(this Vector2 vec, Vector2 ifZero, float length) + { + if (vec == Vector2.Zero) + return ifZero * length; + else + { + vec.Normalize(); + return vec * length; + } + } + + public static float ReflectAngle(float angle, float axis = 0) + { + return -(angle + axis) - axis; + } + + public static float ReflectAngle(float angleRadians, Vector2 axis) + { + return ReflectAngle(angleRadians, axis.Angle()); + } + + public static Vector2 ClosestPointOnLine(Vector2 lineA, Vector2 lineB, Vector2 closestTo) + { + Vector2 v = lineB - lineA; + Vector2 w = closestTo - lineA; + float t = Vector2.Dot(w, v) / Vector2.Dot(v, v); + t = MathHelper.Clamp(t, 0, 1); + + return lineA + v * t; + } + + public static Vector2 Round(this Vector2 vec) + { + return new Vector2((float)Math.Round(vec.X), (float)Math.Round(vec.Y)); + } + + public static float Snap(float value, float increment) + { + return (float)Math.Round(value / increment) * increment; + } + + public static float Snap(float value, float increment, float offset) + { + return ((float)Math.Round((value - offset) / increment) * increment) + offset; + } + + public static float WrapAngleDeg(float angleDegrees) + { + return (((angleDegrees * Math.Sign(angleDegrees) + 180) % 360) - 180) * Math.Sign(angleDegrees); + } + + public static float WrapAngle(float angleRadians) + { + return (((angleRadians * Math.Sign(angleRadians) + MathHelper.Pi) % (MathHelper.Pi * 2)) - MathHelper.Pi) * Math.Sign(angleRadians); + } + + public static Vector2 AngleToVector(float angleRadians, float length) + { + return new Vector2((float)Math.Cos(angleRadians) * length, (float)Math.Sin(angleRadians) * length); + } + + public static float AngleApproach(float val, float target, float maxMove) + { + var diff = AngleDiff(val, target); + if (Math.Abs(diff) < maxMove) + return target; + return val + MathHelper.Clamp(diff, -maxMove, maxMove); + } + + public static float AngleLerp(float startAngle, float endAngle, float percent) + { + return startAngle + AngleDiff(startAngle, endAngle) * percent; + } + + public static float Approach(float val, float target, float maxMove) + { + return val > target ? Math.Max(val - maxMove, target) : Math.Min(val + maxMove, target); + } + + public static float AngleDiff(float radiansA, float radiansB) + { + float diff = radiansB - radiansA; + + while (diff > MathHelper.Pi) { diff -= MathHelper.TwoPi; } + while (diff <= -MathHelper.Pi) { diff += MathHelper.TwoPi; } + + return diff; + } + + public static float AbsAngleDiff(float radiansA, float radiansB) + { + return Math.Abs(AngleDiff(radiansA, radiansB)); + } + + public static int SignAngleDiff(float radiansA, float radiansB) + { + return Math.Sign(AngleDiff(radiansA, radiansB)); + } + + public static float Angle(Vector2 from, Vector2 to) + { + return (float)Math.Atan2(to.Y - from.Y, to.X - from.X); + } + + public static Color ToggleColors(Color current, Color a, Color b) + { + if (current == a) + return b; + else + return a; + } + + public static float ShorterAngleDifference(float currentAngle, float angleA, float angleB) + { + if (Math.Abs(Calc.AngleDiff(currentAngle, angleA)) < Math.Abs(Calc.AngleDiff(currentAngle, angleB))) + return angleA; + else + return angleB; + } + + public static float ShorterAngleDifference(float currentAngle, float angleA, float angleB, float angleC) + { + if (Math.Abs(Calc.AngleDiff(currentAngle, angleA)) < Math.Abs(Calc.AngleDiff(currentAngle, angleB))) + return ShorterAngleDifference(currentAngle, angleA, angleC); + else + return ShorterAngleDifference(currentAngle, angleB, angleC); + } + + public static bool IsInRange(this T[] array, int index) + { + return index >= 0 && index < array.Length; + } + + public static bool IsInRange(this List list, int index) + { + return index >= 0 && index < list.Count; + } + + public static T[] Array(params T[] items) + { + return items; + } + + public static T[] VerifyLength(this T[] array, int length) + { + if (array == null) + return new T[length]; + else if (array.Length != length) + { + T[] newArray = new T[length]; + for (int i = 0; i < Math.Min(length, array.Length); i++) + newArray[i] = array[i]; + return newArray; + } + else + return array; + } + + public static T[][] VerifyLength(this T[][] array, int length0, int length1) + { + array = VerifyLength(array, length0); + for (int i = 0; i < array.Length; i++) + array[i] = VerifyLength(array[i], length1); + return array; + } + + public static bool BetweenInterval(float val, float interval) + { + return val % (interval * 2) > interval; + } + + public static bool OnInterval(float val, float prevVal, float interval) + { + return (int)(prevVal / interval) != (int)(val / interval); + } + + + #endregion + + #region Vector2 + + public static Vector2 Toward(Vector2 from, Vector2 to, float length) + { + if (from == to) + return Vector2.Zero; + else + return (to - from).SafeNormalize(length); + } + + public static Vector2 Toward(Entity from, Entity to, float length) + { + return Toward(from.Position, to.Position, length); + } + + public static Vector2 Perpendicular(this Vector2 vector) + { + return new Vector2(-vector.Y, vector.X); + } + + public static float Angle(this Vector2 vector) + { + return (float)Math.Atan2(vector.Y, vector.X); + } + + public static Vector2 Clamp(this Vector2 val, float minX, float minY, float maxX, float maxY) + { + return new Vector2(MathHelper.Clamp(val.X, minX, maxX), MathHelper.Clamp(val.Y, minY, maxY)); + } + + public static Vector2 Floor(this Vector2 val) + { + return new Vector2((int)Math.Floor(val.X), (int)Math.Floor(val.Y)); + } + + public static Vector2 Ceiling(this Vector2 val) + { + return new Vector2((int)Math.Ceiling(val.X), (int)Math.Ceiling(val.Y)); + } + + public static Vector2 Abs(this Vector2 val) + { + return new Vector2(Math.Abs(val.X), Math.Abs(val.Y)); + } + + public static Vector2 Approach(Vector2 val, Vector2 target, float maxMove) + { + if (maxMove == 0 || val == target) + return val; + + Vector2 diff = target - val; + float length = diff.Length(); + + if (length < maxMove) + return target; + else + { + diff.Normalize(); + return val + diff * maxMove; + } + } + + public static Vector2 FourWayNormal(this Vector2 vec) + { + if (vec == Vector2.Zero) + return Vector2.Zero; + + float angle = vec.Angle(); + angle = (float)Math.Floor((angle + MathHelper.PiOver2 / 2f) / MathHelper.PiOver2) * MathHelper.PiOver2; + + vec = AngleToVector(angle, 1f); + if (Math.Abs(vec.X) < .5f) + vec.X = 0; + else + vec.X = Math.Sign(vec.X); + + if (Math.Abs(vec.Y) < .5f) + vec.Y = 0; + else + vec.Y = Math.Sign(vec.Y); + + return vec; + } + + public static Vector2 EightWayNormal(this Vector2 vec) + { + if (vec == Vector2.Zero) + return Vector2.Zero; + + float angle = vec.Angle(); + angle = (float)Math.Floor((angle + MathHelper.PiOver4 / 2f) / MathHelper.PiOver4) * MathHelper.PiOver4; + + vec = AngleToVector(angle, 1f); + if (Math.Abs(vec.X) < .5f) + vec.X = 0; + else if (Math.Abs(vec.Y) < .5f) + vec.Y = 0; + + return vec; + } + + public static Vector2 SnappedNormal(this Vector2 vec, float slices) + { + float divider = MathHelper.TwoPi / slices; + + float angle = vec.Angle(); + angle = (float)Math.Floor((angle + divider / 2f) / divider) * divider; + return AngleToVector(angle, 1f); + } + + public static Vector2 Snapped(this Vector2 vec, float slices) + { + float divider = MathHelper.TwoPi / slices; + + float angle = vec.Angle(); + angle = (float)Math.Floor((angle + divider / 2f) / divider) * divider; + return AngleToVector(angle, vec.Length()); + } + + public static Vector2 XComp(this Vector2 vec) + { + return Vector2.UnitX * vec.X; + } + + public static Vector2 YComp(this Vector2 vec) + { + return Vector2.UnitY * vec.Y; + } + + public static Vector2[] ParseVector2List(string list, char seperator = '|') + { + var entries = list.Split(seperator); + var data = new Vector2[entries.Length]; + + for (int i = 0; i < entries.Length; i++) + { + var sides = entries[i].Split(','); + data[i] = new Vector2(Convert.ToInt32(sides[0]), Convert.ToInt32(sides[1])); + } + + return data; + } + + #endregion + + #region Vector3 / Quaternion + + public static Vector2 Rotate(this Vector2 vec, float angleRadians) + { + return AngleToVector(vec.Angle() + angleRadians, vec.Length()); + } + + public static Vector2 RotateTowards(this Vector2 vec, float targetAngleRadians, float maxMoveRadians) + { + float angle = AngleApproach(vec.Angle(), targetAngleRadians, maxMoveRadians); + return AngleToVector(angle, vec.Length()); + } + + public static Vector3 RotateTowards(this Vector3 from, Vector3 target, float maxRotationRadians) + { + var c = Vector3.Cross(from, target); + var alen = from.Length(); + var blen = target.Length(); + var w = (float)Math.Sqrt((alen * alen) * (blen * blen)) + Vector3.Dot(from, target); + var q = new Quaternion(c.X, c.Y, c.Z, w); + + if (q.Length() <= maxRotationRadians) + return target; + + q.Normalize(); + q *= maxRotationRadians; + + return Vector3.Transform(from, q); + } + + public static Vector2 XZ(this Vector3 vector) + { + return new Vector2(vector.X, vector.Z); + } + + + public static Vector3 Approach(this Vector3 v, Vector3 target, float amount) + { + if (amount > (target - v).Length()) + return target; + return v + (target - v).SafeNormalize() * amount; + } + + public static Vector3 SafeNormalize(this Vector3 v) + { + var len = v.Length(); + if (len > 0) + return v / len; + return Vector3.Zero; + } + + #endregion + + #region CSV + + public static int[,] ReadCSVIntGrid(string csv, int width, int height) + { + int[,] data = new int[width, height]; + + for (int x = 0; x < width; x++) + for (int y = 0; y < height; y++) + data[x, y] = -1; + + string[] lines = csv.Split('\n'); + for (int y = 0; y < height && y < lines.Length; y++) + { + string[] line = lines[y].Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries); + for (int x = 0; x < width && x < line.Length; x++) + data[x, y] = Convert.ToInt32(line[x]); + } + + return data; + } + + public static int[] ReadCSVInt(string csv) + { + if (csv == "") + return new int[0]; + + string[] values = csv.Split(','); + int[] ret = new int[values.Length]; + + for (int i = 0; i < values.Length; i++) + ret[i] = Convert.ToInt32(values[i].Trim()); + + return ret; + } + + /// + /// Read positive-integer CSV with some added tricks. + /// Use - to add inclusive range. Ex: 3-6 = 3,4,5,6 + /// Use * to add multiple values. Ex: 4*3 = 4,4,4 + /// + /// + /// + public static int[] ReadCSVIntWithTricks(string csv) + { + if (csv == "") + return new int[0]; + + string[] values = csv.Split(','); + List ret = new List(); + + foreach (var val in values) + { + if (val.IndexOf('-') != -1) + { + var split = val.Split('-'); + int a = Convert.ToInt32(split[0]); + int b = Convert.ToInt32(split[1]); + + for (int i = a; i != b; i += Math.Sign(b - a)) + ret.Add(i); + ret.Add(b); + } + else if (val.IndexOf('*') != -1) + { + var split = val.Split('*'); + int a = Convert.ToInt32(split[0]); + int b = Convert.ToInt32(split[1]); + + for (int i = 0; i < b; i++) + ret.Add(a); + } + else + ret.Add(Convert.ToInt32(val)); + } + + return ret.ToArray(); + } + + public static string[] ReadCSV(string csv) + { + if (csv == "") + return new string[0]; + + string[] values = csv.Split(','); + for (int i = 0; i < values.Length; i++) + values[i] = values[i].Trim(); + + return values; + } + + public static string IntGridToCSV(int[,] data) + { + StringBuilder str = new StringBuilder(); + + List line = new List(); + int newLines = 0; + + for (int y = 0; y < data.GetLength(1); y++) + { + int empties = 0; + + for (int x = 0; x < data.GetLength(0); x++) + { + if (data[x, y] == -1) + empties++; + else + { + for (int i = 0; i < newLines; i++) + str.Append('\n'); + for (int i = 0; i < empties; i++) + line.Add(-1); + empties = newLines = 0; + + line.Add(data[x, y]); + } + } + + if (line.Count > 0) + { + str.Append(string.Join(",", line)); + line.Clear(); + } + + newLines++; + } + + return str.ToString(); + } + + #endregion + + #region Data Parse + + public static bool[,] GetBitData(string data, char rowSep = '\n') + { + int lengthX = 0; + for (int i = 0; i < data.Length; i++) + { + if (data[i] == '1' || data[i] == '0') + lengthX++; + else if (data[i] == rowSep) + break; + } + + int lengthY = data.Count(c => c == '\n') + 1; + + bool[,] bitData = new bool[lengthX, lengthY]; + int x = 0; + int y = 0; + for (int i = 0; i < data.Length; i++) + { + switch (data[i]) + { + case '1': + bitData[x, y] = true; + x++; + break; + + case '0': + bitData[x, y] = false; + x++; + break; + + case '\n': + x = 0; + y++; + break; + + default: + break; + } + } + + return bitData; + } + + public static void CombineBitData(bool[,] combineInto, string data, char rowSep = '\n') + { + int x = 0; + int y = 0; + for (int i = 0; i < data.Length; i++) + { + switch (data[i]) + { + case '1': + combineInto[x, y] = true; + x++; + break; + + case '0': + x++; + break; + + case '\n': + x = 0; + y++; + break; + + default: + break; + } + } + } + + public static void CombineBitData(bool[,] combineInto, bool[,] data) + { + for (int i = 0; i < combineInto.GetLength(0); i++) + for (int j = 0; j < combineInto.GetLength(1); j++) + if (data[i, j]) + combineInto[i, j] = true; + } + + public static int[] ConvertStringArrayToIntArray(string[] strings) + { + int[] ret = new int[strings.Length]; + for (int i = 0; i < strings.Length; i++) + ret[i] = Convert.ToInt32(strings[i]); + return ret; + } + + public static float[] ConvertStringArrayToFloatArray(string[] strings) + { + float[] ret = new float[strings.Length]; + for (int i = 0; i < strings.Length; i++) + ret[i] = Convert.ToSingle(strings[i], CultureInfo.InvariantCulture); + return ret; + } + + #endregion + + #region Save and Load Data + + public static bool FileExists(string filename) + { + return File.Exists(filename); + } + + public static bool SaveFile(T obj, string filename) where T : new() + { + Stream stream = new FileStream(filename, FileMode.Create, FileAccess.Write, FileShare.None); + + try + { + XmlSerializer serializer = new XmlSerializer(typeof(T)); + serializer.Serialize(stream, obj); + stream.Close(); + return true; + } + catch + { + stream.Close(); + return false; + } + } + + public static bool LoadFile(string filename, ref T data) where T : new() + { + Stream stream = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read); + + try + { + XmlSerializer serializer = new XmlSerializer(typeof(T)); + T obj = (T)serializer.Deserialize(stream); + stream.Close(); + data = obj; + return true; + } + catch + { + stream.Close(); + return false; + } + } + + #endregion + + #region XML + + public static XmlDocument LoadContentXML(string filename) + { + XmlDocument xml = new XmlDocument(); + xml.Load(TitleContainer.OpenStream(Path.Combine(Engine.Instance.Content.RootDirectory, filename))); + return xml; + } + + public static XmlDocument LoadXML(string filename) + { + XmlDocument xml = new XmlDocument(); + using (var stream = File.OpenRead(filename)) + xml.Load(stream); + return xml; + } + + public static bool ContentXMLExists(string filename) + { + return File.Exists(Path.Combine(Engine.ContentDirectory, filename)); + } + + public static bool XMLExists(string filename) + { + return File.Exists(filename); + } + + #region Attributes + + public static bool HasAttr(this XmlElement xml, string attributeName) + { + return xml.Attributes[attributeName] != null; + } + + public static string Attr(this XmlElement xml, string attributeName) + { +#if DEBUG + if (!xml.HasAttr(attributeName)) + throw new Exception("Element does not contain the attribute \"" + attributeName + "\""); +#endif + return xml.Attributes[attributeName].InnerText; + } + + public static string Attr(this XmlElement xml, string attributeName, string defaultValue) + { + if (!xml.HasAttr(attributeName)) + return defaultValue; + else + return xml.Attributes[attributeName].InnerText; + } + + public static int AttrInt(this XmlElement xml, string attributeName) + { +#if DEBUG + if (!xml.HasAttr(attributeName)) + throw new Exception("Element does not contain the attribute \"" + attributeName + "\""); +#endif + return Convert.ToInt32(xml.Attributes[attributeName].InnerText); + } + + public static int AttrInt(this XmlElement xml, string attributeName, int defaultValue) + { + if (!xml.HasAttr(attributeName)) + return defaultValue; + else + return Convert.ToInt32(xml.Attributes[attributeName].InnerText); + } + + public static float AttrFloat(this XmlElement xml, string attributeName) + { +#if DEBUG + if (!xml.HasAttr(attributeName)) + throw new Exception("Element does not contain the attribute \"" + attributeName + "\""); +#endif + return Convert.ToSingle(xml.Attributes[attributeName].InnerText, CultureInfo.InvariantCulture); + } + + public static float AttrFloat(this XmlElement xml, string attributeName, float defaultValue) + { + if (!xml.HasAttr(attributeName)) + return defaultValue; + else + return Convert.ToSingle(xml.Attributes[attributeName].InnerText, CultureInfo.InvariantCulture); + } + + public static Vector3 AttrVector3(this XmlElement xml, string attributeName) + { + var attr = xml.Attr(attributeName).Split(','); + var x = float.Parse(attr[0].Trim(), CultureInfo.InvariantCulture); + var y = float.Parse(attr[1].Trim(), CultureInfo.InvariantCulture); + var z = float.Parse(attr[2].Trim(), CultureInfo.InvariantCulture); + + return new Vector3(x, y, z); + } + + public static Vector2 AttrVector2(this XmlElement xml, string xAttributeName, string yAttributeName) + { + return new Vector2(xml.AttrFloat(xAttributeName), xml.AttrFloat(yAttributeName)); + } + + public static Vector2 AttrVector2(this XmlElement xml, string xAttributeName, string yAttributeName, Vector2 defaultValue) + { + return new Vector2(xml.AttrFloat(xAttributeName, defaultValue.X), xml.AttrFloat(yAttributeName, defaultValue.Y)); + } + + public static bool AttrBool(this XmlElement xml, string attributeName) + { +#if DEBUG + if (!xml.HasAttr(attributeName)) + throw new Exception("Element does not contain the attribute \"" + attributeName + "\""); +#endif + return Convert.ToBoolean(xml.Attributes[attributeName].InnerText); + } + + public static bool AttrBool(this XmlElement xml, string attributeName, bool defaultValue) + { + if (!xml.HasAttr(attributeName)) + return defaultValue; + else + return AttrBool(xml, attributeName); + } + + public static char AttrChar(this XmlElement xml, string attributeName) + { +#if DEBUG + if (!xml.HasAttr(attributeName)) + throw new Exception("Element does not contain the attribute \"" + attributeName + "\""); +#endif + return Convert.ToChar(xml.Attributes[attributeName].InnerText); + } + + public static char AttrChar(this XmlElement xml, string attributeName, char defaultValue) + { + if (!xml.HasAttr(attributeName)) + return defaultValue; + else + return AttrChar(xml, attributeName); + } + + public static T AttrEnum(this XmlElement xml, string attributeName) where T : struct + { +#if DEBUG + if (!xml.HasAttr(attributeName)) + throw new Exception("Element does not contain the attribute \"" + attributeName + "\""); +#endif + if (Enum.IsDefined(typeof(T), xml.Attributes[attributeName].InnerText)) + return (T)Enum.Parse(typeof(T), xml.Attributes[attributeName].InnerText); + else + throw new Exception("The attribute value cannot be converted to the enum type."); + } + + public static T AttrEnum(this XmlElement xml, string attributeName, T defaultValue) where T : struct + { + if (!xml.HasAttr(attributeName)) + return defaultValue; + else + return xml.AttrEnum(attributeName); + } + + public static Color AttrHexColor(this XmlElement xml, string attributeName) + { +#if DEBUG + if (!xml.HasAttr(attributeName)) + throw new Exception("Element does not contain the attribute \"" + attributeName + "\""); +#endif + return Calc.HexToColor(xml.Attr(attributeName)); + } + + public static Color AttrHexColor(this XmlElement xml, string attributeName, Color defaultValue) + { + if (!xml.HasAttr(attributeName)) + return defaultValue; + else + return AttrHexColor(xml, attributeName); + } + + public static Color AttrHexColor(this XmlElement xml, string attributeName, string defaultValue) + { + if (!xml.HasAttr(attributeName)) + return Calc.HexToColor(defaultValue); + else + return AttrHexColor(xml, attributeName); + } + + public static Vector2 Position(this XmlElement xml) + { + return new Vector2(xml.AttrFloat("x"), xml.AttrFloat("y")); + } + + public static Vector2 Position(this XmlElement xml, Vector2 defaultPosition) + { + return new Vector2(xml.AttrFloat("x", defaultPosition.X), xml.AttrFloat("y", defaultPosition.Y)); + } + + public static int X(this XmlElement xml) + { + return xml.AttrInt("x"); + } + + public static int X(this XmlElement xml, int defaultX) + { + return xml.AttrInt("x", defaultX); + } + + public static int Y(this XmlElement xml) + { + return xml.AttrInt("y"); + } + + public static int Y(this XmlElement xml, int defaultY) + { + return xml.AttrInt("y", defaultY); + } + + public static int Width(this XmlElement xml) + { + return xml.AttrInt("width"); + } + + public static int Width(this XmlElement xml, int defaultWidth) + { + return xml.AttrInt("width", defaultWidth); + } + + public static int Height(this XmlElement xml) + { + return xml.AttrInt("height"); + } + + public static int Height(this XmlElement xml, int defaultHeight) + { + return xml.AttrInt("height", defaultHeight); + } + + public static Rectangle Rect(this XmlElement xml) + { + return new Rectangle(xml.X(), xml.Y(), xml.Width(), xml.Height()); + } + + public static int ID(this XmlElement xml) + { + return xml.AttrInt("id"); + } + + #endregion + + #region Inner Text + + public static int InnerInt(this XmlElement xml) + { + return Convert.ToInt32(xml.InnerText); + } + + public static float InnerFloat(this XmlElement xml) + { + return Convert.ToSingle(xml.InnerText, CultureInfo.InvariantCulture); + } + + public static bool InnerBool(this XmlElement xml) + { + return Convert.ToBoolean(xml.InnerText); + } + + public static T InnerEnum(this XmlElement xml) where T : struct + { + if (Enum.IsDefined(typeof(T), xml.InnerText)) + return (T)Enum.Parse(typeof(T), xml.InnerText); + else + throw new Exception("The attribute value cannot be converted to the enum type."); + } + + public static Color InnerHexColor(this XmlElement xml) + { + return Calc.HexToColor(xml.InnerText); + } + + #endregion + + #region Child Inner Text + + public static bool HasChild(this XmlElement xml, string childName) + { + return xml[childName] != null; + } + + public static string ChildText(this XmlElement xml, string childName) + { +#if DEBUG + if (!xml.HasChild(childName)) + throw new Exception("Cannot find child xml tag with name '" + childName + "'."); +#endif + return xml[childName].InnerText; + } + + public static string ChildText(this XmlElement xml, string childName, string defaultValue) + { + if (xml.HasChild(childName)) + return xml[childName].InnerText; + else + return defaultValue; + } + + public static int ChildInt(this XmlElement xml, string childName) + { +#if DEBUG + if (!xml.HasChild(childName)) + throw new Exception("Cannot find child xml tag with name '" + childName + "'."); +#endif + return xml[childName].InnerInt(); + } + + public static int ChildInt(this XmlElement xml, string childName, int defaultValue) + { + if (xml.HasChild(childName)) + return xml[childName].InnerInt(); + else + return defaultValue; + } + + public static float ChildFloat(this XmlElement xml, string childName) + { +#if DEBUG + if (!xml.HasChild(childName)) + throw new Exception("Cannot find child xml tag with name '" + childName + "'."); +#endif + return xml[childName].InnerFloat(); + } + + public static float ChildFloat(this XmlElement xml, string childName, float defaultValue) + { + if (xml.HasChild(childName)) + return xml[childName].InnerFloat(); + else + return defaultValue; + } + + public static bool ChildBool(this XmlElement xml, string childName) + { +#if DEBUG + if (!xml.HasChild(childName)) + throw new Exception("Cannot find child xml tag with name '" + childName + "'."); +#endif + return xml[childName].InnerBool(); + } + + public static bool ChildBool(this XmlElement xml, string childName, bool defaultValue) + { + if (xml.HasChild(childName)) + return xml[childName].InnerBool(); + else + return defaultValue; + } + + public static T ChildEnum(this XmlElement xml, string childName) where T : struct + { +#if DEBUG + if (!xml.HasChild(childName)) + throw new Exception("Cannot find child xml tag with name '" + childName + "'."); +#endif + if (Enum.IsDefined(typeof(T), xml[childName].InnerText)) + return (T)Enum.Parse(typeof(T), xml[childName].InnerText); + else + throw new Exception("The attribute value cannot be converted to the enum type."); + } + + public static T ChildEnum(this XmlElement xml, string childName, T defaultValue) where T : struct + { + if (xml.HasChild(childName)) + { + if (Enum.IsDefined(typeof(T), xml[childName].InnerText)) + return (T)Enum.Parse(typeof(T), xml[childName].InnerText); + else + throw new Exception("The attribute value cannot be converted to the enum type."); + } + else + return defaultValue; + } + + public static Color ChildHexColor(this XmlElement xml, string childName) + { +#if DEBUG + if (!xml.HasChild(childName)) + throw new Exception("Cannot find child xml tag with name '" + childName + "'."); +#endif + return Calc.HexToColor(xml[childName].InnerText); + } + + public static Color ChildHexColor(this XmlElement xml, string childName, Color defaultValue) + { + if (xml.HasChild(childName)) + return Calc.HexToColor(xml[childName].InnerText); + else + return defaultValue; + } + + public static Color ChildHexColor(this XmlElement xml, string childName, string defaultValue) + { + if (xml.HasChild(childName)) + return Calc.HexToColor(xml[childName].InnerText); + else + return Calc.HexToColor(defaultValue); + } + + public static Vector2 ChildPosition(this XmlElement xml, string childName) + { +#if DEBUG + if (!xml.HasChild(childName)) + throw new Exception("Cannot find child xml tag with name '" + childName + "'."); +#endif + return xml[childName].Position(); + } + + public static Vector2 ChildPosition(this XmlElement xml, string childName, Vector2 defaultValue) + { + if (xml.HasChild(childName)) + return xml[childName].Position(defaultValue); + else + return defaultValue; + } + + #endregion + + #region Ogmo Nodes + + public static Vector2 FirstNode(this XmlElement xml) + { + if (xml["node"] == null) + return Vector2.Zero; + else + return new Vector2((int)xml["node"].AttrFloat("x"), (int)xml["node"].AttrFloat("y")); + } + + public static Vector2? FirstNodeNullable(this XmlElement xml) + { + if (xml["node"] == null) + return null; + else + return new Vector2((int)xml["node"].AttrFloat("x"), (int)xml["node"].AttrFloat("y")); + } + + public static Vector2? FirstNodeNullable(this XmlElement xml, Vector2 offset) + { + if (xml["node"] == null) + return null; + else + return new Vector2((int)xml["node"].AttrFloat("x"), (int)xml["node"].AttrFloat("y")) + offset; + } + + public static Vector2[] Nodes(this XmlElement xml, bool includePosition = false) + { + XmlNodeList nodes = xml.GetElementsByTagName("node"); + if (nodes == null) + return includePosition ? new Vector2[] { xml.Position() } : new Vector2[0]; + + Vector2[] ret; + if (includePosition) + { + ret = new Vector2[nodes.Count + 1]; + ret[0] = xml.Position(); + for (int i = 0; i < nodes.Count; i++) + ret[i + 1] = new Vector2(Convert.ToInt32(nodes[i].Attributes["x"].InnerText), Convert.ToInt32(nodes[i].Attributes["y"].InnerText)); + } + else + { + ret = new Vector2[nodes.Count]; + for (int i = 0; i < nodes.Count; i++) + ret[i] = new Vector2(Convert.ToInt32(nodes[i].Attributes["x"].InnerText), Convert.ToInt32(nodes[i].Attributes["y"].InnerText)); + } + + return ret; + } + + public static Vector2[] Nodes(this XmlElement xml, Vector2 offset, bool includePosition = false) + { + var nodes = Calc.Nodes(xml, includePosition); + + for (int i = 0; i < nodes.Length; i++) + nodes[i] += offset; + + return nodes; + } + + public static Vector2 GetNode(this XmlElement xml, int nodeNum) + { + return xml.Nodes()[nodeNum]; + } + + public static Vector2? GetNodeNullable(this XmlElement xml, int nodeNum) + { + if (xml.Nodes().Length > nodeNum) + return xml.Nodes()[nodeNum]; + else + return null; + } + + #endregion + + #region Add Stuff + + public static void SetAttr(this XmlElement xml, string attributeName, Object setTo) + { + XmlAttribute attr; + + if (xml.HasAttr(attributeName)) + attr = xml.Attributes[attributeName]; + else + { + attr = xml.OwnerDocument.CreateAttribute(attributeName); + xml.Attributes.Append(attr); + } + + attr.Value = setTo.ToString(); + } + + public static void SetChild(this XmlElement xml, string childName, Object setTo) + { + XmlElement ele; + + if (xml.HasChild(childName)) + ele = xml[childName]; + else + { + ele = xml.OwnerDocument.CreateElement(null, childName, xml.NamespaceURI); + xml.AppendChild(ele); + } + + ele.InnerText = setTo.ToString(); + } + + public static XmlElement CreateChild(this XmlDocument doc, string childName) + { + XmlElement ele = doc.CreateElement(null, childName, doc.NamespaceURI); + doc.AppendChild(ele); + return ele; + } + + public static XmlElement CreateChild(this XmlElement xml, string childName) + { + XmlElement ele = xml.OwnerDocument.CreateElement(null, childName, xml.NamespaceURI); + xml.AppendChild(ele); + return ele; + } + + #endregion + + #endregion + + #region Sorting + + public static int SortLeftToRight(Entity a, Entity b) + { + return (int)((a.X - b.X) * 100); + } + + public static int SortRightToLeft(Entity a, Entity b) + { + return (int)((b.X - a.X) * 100); + } + + public static int SortTopToBottom(Entity a, Entity b) + { + return (int)((a.Y - b.Y) * 100); + } + + public static int SortBottomToTop(Entity a, Entity b) + { + return (int)((b.Y - a.Y) * 100); + } + + public static int SortByDepth(Entity a, Entity b) + { + return a.Depth - b.Depth; + } + + public static int SortByDepthReversed(Entity a, Entity b) + { + return b.Depth - a.Depth; + } + + #endregion + + #region Debug + + public static void Log() + { + Debug.WriteLine("Log"); + } + + public static void TimeLog() + { + Debug.WriteLine(Engine.Scene.RawTimeActive); + } + + public static void Log(params object[] obj) + { + foreach (var o in obj) + { + if (o == null) + Debug.WriteLine("null"); + else + Debug.WriteLine(o.ToString()); + } + } + + public static void TimeLog(object obj) + { + Debug.WriteLine(Engine.Scene.RawTimeActive + " : " + obj); + } + + public static void LogEach(IEnumerable collection) + { + foreach (var o in collection) + Debug.WriteLine(o.ToString()); + } + + public static void Dissect(Object obj) + { + Debug.Write(obj.GetType().Name + " { "); + foreach (var v in obj.GetType().GetFields()) + Debug.Write(v.Name + ": " + v.GetValue(obj) + ", "); + Debug.WriteLine(" }"); + } + + private static Stopwatch stopwatch; + + public static void StartTimer() + { + stopwatch = new Stopwatch(); + stopwatch.Start(); + } + + public static void EndTimer() + { + if (stopwatch != null) + { + stopwatch.Stop(); + + string message = "Timer: " + stopwatch.ElapsedTicks + " ticks, or " + TimeSpan.FromTicks(stopwatch.ElapsedTicks).TotalSeconds.ToString("00.0000000") + " seconds"; + Debug.WriteLine(message); +#if DESKTOP && DEBUG + //Commands.Trace(message); +#endif + stopwatch = null; + } + } + + #endregion + + #region Reflection + + public static Delegate GetMethod(Object obj, string method) where T : class + { + var info = obj.GetType().GetMethod(method, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); + if (info == null) + return null; + else + return Delegate.CreateDelegate(typeof(T), obj, method); + } + + #endregion + + public static T At(this T[,] arr, Pnt at) + { + return arr[at.X, at.Y]; + } + + public static string ConvertPath(string path) + { + return path.Replace('/', Path.DirectorySeparatorChar).Replace('\\', Path.DirectorySeparatorChar); + } + + public static string ReadNullTerminatedString(this System.IO.BinaryReader stream) + { + string str = ""; + char ch; + while ((int)(ch = stream.ReadChar()) != 0) + str = str + ch; + return str; + } + + public static IEnumerator Do(params IEnumerator[] numerators) + { + if (numerators.Length == 0) + yield break; + else if (numerators.Length == 1) + yield return numerators[0]; + else + { + List routines = new List(); + foreach (var enumerator in numerators) + routines.Add(new Coroutine(enumerator)); + + while (true) + { + bool moving = false; + foreach (var routine in routines) + { + routine.Update(); + if (!routine.Finished) + moving = true; + } + + if (moving) + yield return null; + else + break; + } + } + } + + public static Rectangle ClampTo(this Rectangle rect, Rectangle clamp) + { + if (rect.X < clamp.X) + { + rect.Width -= (clamp.X - rect.X); + rect.X = clamp.X; + } + + if (rect.Y < clamp.Y) + { + rect.Height -= (clamp.Y - rect.Y); + rect.Y = clamp.Y; + } + + if (rect.Right > clamp.Right) + rect.Width = clamp.Right - rect.X; + if (rect.Bottom > clamp.Bottom) + rect.Height = clamp.Bottom - rect.Y; + + return rect; + } + } + + public static class QuaternionExt + { + public static Quaternion Conjugated(this Quaternion q) + { + var c = q; + c.Conjugate(); + return c; + } + + public static Quaternion LookAt(this Quaternion q, Vector3 from, Vector3 to, Vector3 up) + { + return Quaternion.CreateFromRotationMatrix(Matrix.CreateLookAt(from, to, up)); + } + + public static Quaternion LookAt(this Quaternion q, Vector3 direction, Vector3 up) + { + return Quaternion.CreateFromRotationMatrix(Matrix.CreateLookAt(Vector3.Zero, direction, up)); + } + + public static Vector3 Forward(this Quaternion q) + { + return Vector3.Transform(Vector3.Forward, q.Conjugated()); + } + + public static Vector3 Left(this Quaternion q) + { + return Vector3.Transform(Vector3.Left, q.Conjugated()); + } + + public static Vector3 Up(this Quaternion q) + { + return Vector3.Transform(Vector3.Up, q.Conjugated()); + } + } +} diff --git a/MonocleEngineDemo/Monocle/Util/Camera.cs b/MonocleEngineDemo/Monocle/Util/Camera.cs new file mode 100644 index 0000000..1f627b1 --- /dev/null +++ b/MonocleEngineDemo/Monocle/Util/Camera.cs @@ -0,0 +1,252 @@ +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using System; + +namespace Monocle +{ + public class Camera + { + private Matrix matrix = Matrix.Identity; + private Matrix inverse = Matrix.Identity; + private bool changed; + + private Vector2 position = Vector2.Zero; + private Vector2 zoom = Vector2.One; + private Vector2 origin = Vector2.Zero; + private float angle = 0; + + public Viewport Viewport; + + public Camera() + { + Viewport = new Viewport(); + Viewport.Width = Engine.Width; + Viewport.Height = Engine.Height; + UpdateMatrices(); + } + + public Camera(int width, int height) + { + Viewport = new Viewport(); + Viewport.Width = width; + Viewport.Height = height; + UpdateMatrices(); + } + + public override string ToString() + { + return "Camera:\n\tViewport: { " + Viewport.X + ", " + Viewport.Y + ", " + Viewport.Width + ", " + Viewport.Height + + " }\n\tPosition: { " + position.X + ", " + position.Y + + " }\n\tOrigin: { " + origin.X + ", " + origin.Y + + " }\n\tZoom: { " + zoom.X + ", " + zoom.Y + + " }\n\tAngle: " + angle; + } + + private void UpdateMatrices() + { + matrix = Matrix.Identity * + Matrix.CreateTranslation(new Vector3(-new Vector2((int)Math.Floor(position.X), (int)Math.Floor(position.Y)), 0)) * + Matrix.CreateRotationZ(angle) * + Matrix.CreateScale(new Vector3(zoom, 1)) * + Matrix.CreateTranslation(new Vector3(new Vector2((int)Math.Floor(origin.X), (int)Math.Floor(origin.Y)), 0)); + + inverse = Matrix.Invert(matrix); + + changed = false; + } + + public void CopyFrom(Camera other) + { + position = other.position; + origin = other.origin; + angle = other.angle; + zoom = other.zoom; + changed = true; + } + + public Matrix Matrix + { + get + { + if (changed) + UpdateMatrices(); + return matrix; + } + } + + public Matrix Inverse + { + get + { + if (changed) + UpdateMatrices(); + return inverse; + } + } + + public Vector2 Position + { + get { return position; } + set + { + changed = true; + position = value; + } + } + + public Vector2 Origin + { + get { return origin; } + set + { + changed = true; + origin = value; + } + } + + public float X + { + get { return position.X; } + set + { + changed = true; + position.X = value; + } + } + + public float Y + { + get { return position.Y; } + set + { + changed = true; + position.Y = value; + } + } + + public float Zoom + { + get { return zoom.X; } + set + { + changed = true; + zoom.X = zoom.Y = value; + } + } + + public float Angle + { + get { return angle; } + set + { + changed = true; + angle = value; + } + } + + public float Left + { + get + { + if (changed) + UpdateMatrices(); + return Vector2.Transform(Vector2.Zero, Inverse).X; + } + + set + { + if (changed) + UpdateMatrices(); + X = Vector2.Transform(Vector2.UnitX * value, Matrix).X; + } + } + + public float Right + { + get + { + if (changed) + UpdateMatrices(); + return Vector2.Transform(Vector2.UnitX * Viewport.Width, Inverse).X; + } + + set + { + throw new NotImplementedException(); + } + } + + public float Top + { + get + { + if (changed) + UpdateMatrices(); + return Vector2.Transform(Vector2.Zero, Inverse).Y; + } + + set + { + if (changed) + UpdateMatrices(); + Y = Vector2.Transform(Vector2.UnitY * value, Matrix).Y; + } + } + + public float Bottom + { + get + { + if (changed) + UpdateMatrices(); + return Vector2.Transform(Vector2.UnitY * Viewport.Height, Inverse).Y; + } + + set + { + throw new NotImplementedException(); + } + } + + /* + * Utils + */ + + public void CenterOrigin() + { + origin = new Vector2((float)Viewport.Width / 2, (float)Viewport.Height / 2); + changed = true; + } + + public void RoundPosition() + { + position.X = (float)Math.Round(position.X); + position.Y = (float)Math.Round(position.Y); + changed = true; + } + + public Vector2 ScreenToCamera(Vector2 position) + { + return Vector2.Transform(position, Inverse); + } + + public Vector2 CameraToScreen(Vector2 position) + { + return Vector2.Transform(position, Matrix); + } + + public void Approach(Vector2 position, float ease) + { + Position += (position - Position) * ease; + } + + public void Approach(Vector2 position, float ease, float maxDistance) + { + Vector2 move = (position - Position) * ease; + if (move.Length() > maxDistance) + Position += Vector2.Normalize(move) * maxDistance; + else + Position += move; + } + } +} \ No newline at end of file diff --git a/MonocleEngineDemo/Monocle/Util/CheatListener.cs b/MonocleEngineDemo/Monocle/Util/CheatListener.cs new file mode 100644 index 0000000..54e8b00 --- /dev/null +++ b/MonocleEngineDemo/Monocle/Util/CheatListener.cs @@ -0,0 +1,75 @@ +using System; +using System.Collections.Generic; + +namespace Monocle +{ + public class CheatListener : Entity + { + public string CurrentInput; + public bool Logging; + + private List>> inputs; + private List> cheats; + private int maxInput; + + public CheatListener() + { + Visible = false; + CurrentInput = ""; + + inputs = new List>>(); + cheats = new List>(); + } + + public override void Update() + { + //Detect input + bool changed = false; + foreach (var input in inputs) + { + if (input.Item2()) + { + CurrentInput += input.Item1; + changed = true; + } + } + + //Handle changes + if (changed) + { + if (CurrentInput.Length > maxInput) + CurrentInput = CurrentInput.Substring(CurrentInput.Length - maxInput); + + if (Logging) + Calc.Log(CurrentInput); + + foreach (var cheat in cheats) + { + if (CurrentInput.Contains(cheat.Item1)) + { + CurrentInput = ""; + if (cheat.Item2 != null) + cheat.Item2(); + cheats.Remove(cheat); + + if (Logging) + Calc.Log("Cheat Activated: " + cheat.Item1); + + break; + } + } + } + } + + public void AddCheat(string code, Action onEntered = null) + { + cheats.Add(new Tuple(code, onEntered)); + maxInput = Math.Max(code.Length, maxInput); + } + + public void AddInput(char id, Func checker) + { + inputs.Add(new Tuple>(id, checker)); + } + } +} diff --git a/MonocleEngineDemo/Monocle/Util/ChoiceSet.cs b/MonocleEngineDemo/Monocle/Util/ChoiceSet.cs new file mode 100644 index 0000000..403a0ac --- /dev/null +++ b/MonocleEngineDemo/Monocle/Util/ChoiceSet.cs @@ -0,0 +1,172 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Monocle +{ + public class ChoiceSet + { + public int TotalWeight { get; private set; } + private Dictionary choices; + + public ChoiceSet() + { + choices = new Dictionary(); + TotalWeight = 0; + } + + /// + /// Sets the weight of a choice + /// + /// + /// + public void Set(T choice, int weight) + { + int oldWeight = 0; + choices.TryGetValue(choice, out oldWeight); + TotalWeight -= oldWeight; + + if (weight <= 0) + { + if (choices.ContainsKey(choice)) + choices.Remove(choice); + } + else + { + TotalWeight += weight; + choices[choice] = weight; + } + } + + /// + /// Sets the weight of a choice, or gets its weight + /// + /// + /// + public int this[T choice] + { + get + { + int weight = 0; + choices.TryGetValue(choice, out weight); + return weight; + } + + set + { + Set(choice, value); + } + } + + /// + /// Sets the chance of a choice. The chance is calculated based on the current state of ChoiceSet, so if + /// other choices are changed later the chance will not be guaranteed to remain the same + /// + /// + /// A chance between 0 and 1.0f + public void Set(T choice, float chance) + { + int oldWeight = 0; + choices.TryGetValue(choice, out oldWeight); + TotalWeight -= oldWeight; + + int weight = (int)Math.Round(TotalWeight / (1f - chance)); + if (weight <= 0 && chance > 0) + weight = 1; + + if (weight <= 0) + { + if (choices.ContainsKey(choice)) + choices.Remove(choice); + } + else + { + TotalWeight += weight; + choices[choice] = weight; + } + } + + /// + /// Sets the chance of many choices. Takes the chance of any of the given choices being picked, not the chance of + /// any individual choice. The chances are calculated based on the current state of ChoiceSet, so if + /// other choices are changed later the chances will not be guaranteed to remain the same + /// + /// + /// A chance between 0 and 1.0f + public void SetMany(float totalChance, params T[] choices) + { + if (choices.Length > 0) + { + float chance = totalChance / choices.Length; + + int oldTotalWeight = 0; + foreach (var c in choices) + { + int oldWeight = 0; + this.choices.TryGetValue(c, out oldWeight); + oldTotalWeight += oldWeight; + } + TotalWeight -= oldTotalWeight; + + int weight = (int)Math.Round((TotalWeight / (1f - totalChance)) / choices.Length); + if (weight <= 0 && totalChance > 0) + weight = 1; + + if (weight <= 0) + { + foreach (var c in choices) + if (this.choices.ContainsKey(c)) + this.choices.Remove(c); + } + else + { + TotalWeight += weight * choices.Length; + foreach (var c in choices) + this.choices[c] = weight; + } + } + } + + /// + /// Chooses a random choice in the set + /// + /// + /// + public T Get(Random random) + { + int at = random.Next(TotalWeight); + + foreach (var kv in choices) + { + if (at < kv.Value) + return kv.Key; + else + at -= kv.Value; + } + + throw new Exception("Random choice error!"); + } + + /// + /// Chooses a random choice in the set, using Calc.Random to choose + /// + /// + public T Get() + { + return Get(Calc.Random); + } + + private struct Choice + { + public T Data; + public int Weight; + + public Choice(T data, int weight) + { + Data = data; + Weight = weight; + } + } + } +} diff --git a/MonocleEngineDemo/Monocle/Util/Chooser.cs b/MonocleEngineDemo/Monocle/Util/Chooser.cs new file mode 100644 index 0000000..e554965 --- /dev/null +++ b/MonocleEngineDemo/Monocle/Util/Chooser.cs @@ -0,0 +1,158 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Monocle +{ + /// + /// Utility class for making weighted random choices from a set. + /// + public class Chooser + { + private List choices; + + public Chooser() + { + choices = new List(); + } + + /// + /// Initialize with a single choice with the given weight. + /// + public Chooser(T firstChoice, float weight) + : this() + { + Add(firstChoice, weight); + } + + /// + /// Initialize with a list of choices, all with a weight of 1. + /// + public Chooser(params T[] choices) + : this() + { + foreach (var choice in choices) + Add(choice, 1); + } + + public int Count + { + get + { + return choices.Count; + } + } + + public T this[int index] + { + get + { + if (index < 0 || index >= Count) + throw new IndexOutOfRangeException(); + + return choices[index].Value; + } + + set + { + if (index < 0 || index >= Count) + throw new IndexOutOfRangeException(); + + choices[index].Value = value; + } + } + + public Chooser Add(T choice, float weight) + { + weight = Math.Max(weight, 0); + choices.Add(new Choice(choice, weight)); + TotalWeight += weight; + return this; + } + + public T Choose() + { + if (TotalWeight <= 0) + return default(T); + else if (choices.Count == 1) + return choices[0].Value; + + var roll = Calc.Random.NextDouble() * TotalWeight; + float check = 0; + + for (int i = 0; i < choices.Count - 1; i++) + { + check += choices[i].Weight; + if (roll < check) + return choices[i].Value; + } + + return choices[choices.Count - 1].Value; + } + + public float TotalWeight + { + get; private set; + } + + public bool CanChoose + { + get + { + return TotalWeight > 0; + } + } + + private class Choice + { + public T Value; + public float Weight; + + public Choice(T value, float weight) + { + Value = value; + Weight = weight; + } + } + + /// + /// Parses a chooser from a string. + /// + /// Choices to parse. Format: "choice0:weight,choice1:weight,..." + /// + public static Chooser FromString(string data) where TT : IConvertible + { + var chooser = new Chooser(); + string[] choices = data.Split(','); + + //If it's just a single choice with no weight, add it and return + if (choices.Length == 1 && choices[0].IndexOf(':') == -1) + { + chooser.Add((TT)Convert.ChangeType(choices[0], typeof(TT)), 1f); + return chooser; + } + + //Parse the individual choices + foreach (var choice in choices) + { + if (choice.IndexOf(':') == -1) + { + //No weight, default to weight of 1 + chooser.Add((TT)Convert.ChangeType(choice, typeof(TT)), 1f); + } + else + { + //Has weight, handle that correctly + var parts = choice.Split(':'); + var key = parts[0].Trim(); + var weight = parts[1].Trim(); + + chooser.Add((TT)Convert.ChangeType(key, typeof(TT)), Convert.ToSingle(weight)); + } + } + + return chooser; + } + } +} diff --git a/MonocleEngineDemo/Monocle/Util/Commands.cs b/MonocleEngineDemo/Monocle/Util/Commands.cs new file mode 100644 index 0000000..4f3c447 --- /dev/null +++ b/MonocleEngineDemo/Monocle/Util/Commands.cs @@ -0,0 +1,844 @@ +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using Microsoft.Xna.Framework.Input; +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Text; + +namespace Monocle +{ + public class Commands + { + private const float UNDERSCORE_TIME = .5f; + private const float REPEAT_DELAY = .5f; + private const float REPEAT_EVERY = 1 / 30f; + private const float OPACITY = .8f; + + public bool Enabled = true; + public bool Open; + public Action[] FunctionKeyActions { get; private set; } + + private Dictionary commands; + private List sorted; + + private KeyboardState oldState; + private KeyboardState currentState; + private string currentText = ""; + private List drawCommands; + private bool underscore; + private float underscoreCounter; + private List commandHistory; + private int seekIndex = -1; + private int tabIndex = -1; + private string tabSearch; + private float repeatCounter = 0; + private Keys? repeatKey = null; + private bool canOpen; + + public Commands() + { + commandHistory = new List(); + drawCommands = new List(); + commands = new Dictionary(); + sorted = new List(); + FunctionKeyActions = new Action[12]; + + BuildCommandsList(); + } + + public void Log(object obj, Color color) + { + string str = obj.ToString(); + + //Newline splits + if (str.Contains("\n")) + { + var all = str.Split('\n'); + foreach (var line in all) + Log(line, color); + return; + } + + //Split the string if you overlow horizontally + int maxWidth = Engine.Instance.Window.ClientBounds.Width - 40; + while (Draw.DefaultFont.MeasureString(str).X > maxWidth) + { + int split = -1; + for (int i = 0; i < str.Length; i++) + { + if (str[i] == ' ') + { + if (Draw.DefaultFont.MeasureString(str.Substring(0, i)).X <= maxWidth) + split = i; + else + break; + } + } + + if (split == -1) + break; + + drawCommands.Insert(0, new Line(str.Substring(0, split), color)); + str = str.Substring(split + 1); + } + + drawCommands.Insert(0, new Line(str, color)); + + //Don't overflow top of window + int maxCommands = (Engine.Instance.Window.ClientBounds.Height - 100) / 30; + while (drawCommands.Count > maxCommands) + drawCommands.RemoveAt(drawCommands.Count - 1); + } + + public void Log(object obj) + { + Log(obj, Color.White); + } + + #region Updating and Rendering + + internal void UpdateClosed() + { + if (!canOpen) + canOpen = true; + else if (MInput.Keyboard.Pressed(Keys.OemTilde, Keys.Oem8)) + { + Open = true; + currentState = Keyboard.GetState(); + } + + for (int i = 0; i < FunctionKeyActions.Length; i++) + if (MInput.Keyboard.Pressed((Keys)(Keys.F1 + i))) + ExecuteFunctionKeyAction(i); + } + + internal void UpdateOpen() + { + oldState = currentState; + currentState = Keyboard.GetState(); + + underscoreCounter += Engine.DeltaTime; + while (underscoreCounter >= UNDERSCORE_TIME) + { + underscoreCounter -= UNDERSCORE_TIME; + underscore = !underscore; + } + + if (repeatKey.HasValue) + { + if (currentState[repeatKey.Value] == KeyState.Down) + { + repeatCounter += Engine.DeltaTime; + + while (repeatCounter >= REPEAT_DELAY) + { + HandleKey(repeatKey.Value); + repeatCounter -= REPEAT_EVERY; + } + } + else + repeatKey = null; + } + + foreach (Keys key in currentState.GetPressedKeys()) + { + if (oldState[key] == KeyState.Up) + { + HandleKey(key); + break; + } + } + } + + private void HandleKey(Keys key) + { + if (key != Keys.Tab && key != Keys.LeftShift && key != Keys.RightShift && key != Keys.RightAlt && key != Keys.LeftAlt && key != Keys.RightControl && key != Keys.LeftControl) + tabIndex = -1; + + if (key != Keys.OemTilde && key != Keys.Oem8 && key != Keys.Enter && repeatKey != key) + { + repeatKey = key; + repeatCounter = 0; + } + + switch (key) + { + default: + if (key.ToString().Length == 1) + { + if (currentState[Keys.LeftShift] == KeyState.Down || currentState[Keys.RightShift] == KeyState.Down) + currentText += key.ToString(); + else + currentText += key.ToString().ToLower(); + } + break; + + case (Keys.D1): + if (currentState[Keys.LeftShift] == KeyState.Down || currentState[Keys.RightShift] == KeyState.Down) + currentText += '!'; + else + currentText += '1'; + break; + case (Keys.D2): + if (currentState[Keys.LeftShift] == KeyState.Down || currentState[Keys.RightShift] == KeyState.Down) + currentText += '@'; + else + currentText += '2'; + break; + case (Keys.D3): + if (currentState[Keys.LeftShift] == KeyState.Down || currentState[Keys.RightShift] == KeyState.Down) + currentText += '#'; + else + currentText += '3'; + break; + case (Keys.D4): + if (currentState[Keys.LeftShift] == KeyState.Down || currentState[Keys.RightShift] == KeyState.Down) + currentText += '$'; + else + currentText += '4'; + break; + case (Keys.D5): + if (currentState[Keys.LeftShift] == KeyState.Down || currentState[Keys.RightShift] == KeyState.Down) + currentText += '%'; + else + currentText += '5'; + break; + case (Keys.D6): + if (currentState[Keys.LeftShift] == KeyState.Down || currentState[Keys.RightShift] == KeyState.Down) + currentText += '^'; + else + currentText += '6'; + break; + case (Keys.D7): + if (currentState[Keys.LeftShift] == KeyState.Down || currentState[Keys.RightShift] == KeyState.Down) + currentText += '&'; + else + currentText += '7'; + break; + case (Keys.D8): + if (currentState[Keys.LeftShift] == KeyState.Down || currentState[Keys.RightShift] == KeyState.Down) + currentText += '*'; + else + currentText += '8'; + break; + case (Keys.D9): + if (currentState[Keys.LeftShift] == KeyState.Down || currentState[Keys.RightShift] == KeyState.Down) + currentText += '('; + else + currentText += '9'; + break; + case (Keys.D0): + if (currentState[Keys.LeftShift] == KeyState.Down || currentState[Keys.RightShift] == KeyState.Down) + currentText += ')'; + else + currentText += '0'; + break; + case (Keys.OemComma): + if (currentState[Keys.LeftShift] == KeyState.Down || currentState[Keys.RightShift] == KeyState.Down) + currentText += '<'; + else + currentText += ','; + break; + case Keys.OemPeriod: + if (currentState[Keys.LeftShift] == KeyState.Down || currentState[Keys.RightShift] == KeyState.Down) + currentText += '>'; + else + currentText += '.'; + break; + case Keys.OemQuestion: + if (currentState[Keys.LeftShift] == KeyState.Down || currentState[Keys.RightShift] == KeyState.Down) + currentText += '?'; + else + currentText += '/'; + break; + case Keys.OemSemicolon: + if (currentState[Keys.LeftShift] == KeyState.Down || currentState[Keys.RightShift] == KeyState.Down) + currentText += ':'; + else + currentText += ';'; + break; + case Keys.OemQuotes: + if (currentState[Keys.LeftShift] == KeyState.Down || currentState[Keys.RightShift] == KeyState.Down) + currentText += '"'; + else + currentText += '\''; + break; + case Keys.OemBackslash: + if (currentState[Keys.LeftShift] == KeyState.Down || currentState[Keys.RightShift] == KeyState.Down) + currentText += '|'; + else + currentText += '\\'; + break; + case Keys.OemOpenBrackets: + if (currentState[Keys.LeftShift] == KeyState.Down || currentState[Keys.RightShift] == KeyState.Down) + currentText += '{'; + else + currentText += '['; + break; + case Keys.OemCloseBrackets: + if (currentState[Keys.LeftShift] == KeyState.Down || currentState[Keys.RightShift] == KeyState.Down) + currentText += '}'; + else + currentText += ']'; + break; + case Keys.OemMinus: + if (currentState[Keys.LeftShift] == KeyState.Down || currentState[Keys.RightShift] == KeyState.Down) + currentText += '_'; + else + currentText += '-'; + break; + case Keys.OemPlus: + if (currentState[Keys.LeftShift] == KeyState.Down || currentState[Keys.RightShift] == KeyState.Down) + currentText += '+'; + else + currentText += '='; + break; + + case Keys.Space: + currentText += " "; + break; + case Keys.Back: + if (currentText.Length > 0) + currentText = currentText.Substring(0, currentText.Length - 1); + break; + case Keys.Delete: + currentText = ""; + break; + + case Keys.Up: + if (seekIndex < commandHistory.Count - 1) + { + seekIndex++; + currentText = string.Join(" ", commandHistory[seekIndex]); + } + break; + case Keys.Down: + if (seekIndex > -1) + { + seekIndex--; + if (seekIndex == -1) + currentText = ""; + else + currentText = string.Join(" ", commandHistory[seekIndex]); + } + break; + + case Keys.Tab: + if (currentState[Keys.LeftShift] == KeyState.Down || currentState[Keys.RightShift] == KeyState.Down) + { + if (tabIndex == -1) + { + tabSearch = currentText; + FindLastTab(); + } + else + { + tabIndex--; + if (tabIndex < 0 || (tabSearch != "" && sorted[tabIndex].IndexOf(tabSearch) != 0)) + FindLastTab(); + } + } + else + { + if (tabIndex == -1) + { + tabSearch = currentText; + FindFirstTab(); + } + else + { + tabIndex++; + if (tabIndex >= sorted.Count || (tabSearch != "" && sorted[tabIndex].IndexOf(tabSearch) != 0)) + FindFirstTab(); + } + } + if (tabIndex != -1) + currentText = sorted[tabIndex]; + break; + + case Keys.F1: + case Keys.F2: + case Keys.F3: + case Keys.F4: + case Keys.F5: + case Keys.F6: + case Keys.F7: + case Keys.F8: + case Keys.F9: + case Keys.F10: + case Keys.F11: + case Keys.F12: + ExecuteFunctionKeyAction((int)(key - Keys.F1)); + break; + + case Keys.Enter: + if (currentText.Length > 0) + EnterCommand(); + break; + + case Keys.Oem8: + case Keys.OemTilde: + Open = canOpen = false; + break; + } + } + + private void EnterCommand() + { + string[] data = currentText.Split(new char[] { ' ', ',' }, StringSplitOptions.RemoveEmptyEntries); + if (commandHistory.Count == 0 || commandHistory[0] != currentText) + commandHistory.Insert(0, currentText); + drawCommands.Insert(0, new Line(currentText, Color.Aqua)); + currentText = ""; + seekIndex = -1; + + string[] args = new string[data.Length - 1]; + for (int i = 1; i < data.Length; i++) + args[i - 1] = data[i]; + ExecuteCommand(data[0].ToLower(), args); + } + + private void FindFirstTab() + { + for (int i = 0; i < sorted.Count; i++) + { + if (tabSearch == "" || sorted[i].IndexOf(tabSearch) == 0) + { + tabIndex = i; + break; + } + } + } + + private void FindLastTab() + { + for (int i = 0; i < sorted.Count; i++) + if (tabSearch == "" || sorted[i].IndexOf(tabSearch) == 0) + tabIndex = i; + } + + internal void Render() + { + int screenWidth = Engine.ViewWidth; + int screenHeight = Engine.ViewHeight; + + Draw.SpriteBatch.Begin(); + + Draw.Rect(10, screenHeight - 50, screenWidth - 20, 40, Color.Black * OPACITY); + if (underscore) + Draw.SpriteBatch.DrawString(Draw.DefaultFont, ">" + currentText + "_", new Vector2(20, screenHeight - 42), Color.White); + else + Draw.SpriteBatch.DrawString(Draw.DefaultFont, ">" + currentText, new Vector2(20, screenHeight - 42), Color.White); + + if (drawCommands.Count > 0) + { + int height = 10 + (30 * drawCommands.Count); + Draw.Rect(10, screenHeight - height - 60, screenWidth - 20, height, Color.Black * OPACITY); + for (int i = 0; i < drawCommands.Count; i++) + Draw.SpriteBatch.DrawString(Draw.DefaultFont, drawCommands[i].Text, new Vector2(20, screenHeight - 92 - (30 * i)), drawCommands[i].Color); + } + + Draw.SpriteBatch.End(); + } + + #endregion + + #region Execute + + public void ExecuteCommand(string command, string[] args) + { + if (commands.ContainsKey(command)) + commands[command].Action(args); + else + Log("Command '" + command + "' not found! Type 'help' for list of commands", Color.Yellow); + } + + public void ExecuteFunctionKeyAction(int num) + { + if (FunctionKeyActions[num] != null) + FunctionKeyActions[num](); + } + + #endregion + + #region Parse Commands + + private void BuildCommandsList() + { +#if !CONSOLE + //Check Monocle for Commands + foreach (var type in Assembly.GetCallingAssembly().GetTypes()) + foreach (var method in type.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)) + ProcessMethod(method); + + //Check the calling assembly for Commands + foreach (var type in Assembly.GetEntryAssembly().GetTypes()) + foreach (var method in type.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)) + ProcessMethod(method); + + //Maintain the sorted command list + foreach (var command in commands) + sorted.Add(command.Key); + sorted.Sort(); +#endif + } + + private void ProcessMethod(MethodInfo method) + { + Command attr = null; + { + var attrs = method.GetCustomAttributes(typeof(Command), false); + if (attrs.Length > 0) + attr = attrs[0] as Command; + } + + if (attr != null) + { + if (!method.IsStatic) + throw new Exception(method.DeclaringType.Name + "." + method.Name + " is marked as a command, but is not static"); + else + { + CommandInfo info = new CommandInfo(); + info.Help = attr.Help; + + var parameters = method.GetParameters(); + var defaults = new object[parameters.Length]; + string[] usage = new string[parameters.Length]; + + for (int i = 0; i < parameters.Length; i++) + { + var p = parameters[i]; + usage[i] = p.Name + ":"; + + if (p.ParameterType == typeof(string)) + usage[i] += "string"; + else if (p.ParameterType == typeof(int)) + usage[i] += "int"; + else if (p.ParameterType == typeof(float)) + usage[i] += "float"; + else if (p.ParameterType == typeof(bool)) + usage[i] += "bool"; + else + throw new Exception(method.DeclaringType.Name + "." + method.Name + " is marked as a command, but has an invalid parameter type. Allowed types are: string, int, float, and bool"); + + if (p.DefaultValue == DBNull.Value) + defaults[i] = null; + else if (p.DefaultValue != null) + { + defaults[i] = p.DefaultValue; + if (p.ParameterType == typeof(string)) + usage[i] += "=\"" + p.DefaultValue + "\""; + else + usage[i] += "=" + p.DefaultValue; + } + else + defaults[i] = null; + } + + if (usage.Length == 0) + info.Usage = ""; + else + info.Usage = "[" + string.Join(" ", usage) + "]"; + + info.Action = (args) => + { + if (parameters.Length == 0) + InvokeMethod(method); + else + { + object[] param = (object[])defaults.Clone(); + + for (int i = 0; i < param.Length && i < args.Length; i++) + { + if (parameters[i].ParameterType == typeof(string)) + param[i] = ArgString(args[i]); + else if (parameters[i].ParameterType == typeof(int)) + param[i] = ArgInt(args[i]); + else if (parameters[i].ParameterType == typeof(float)) + param[i] = ArgFloat(args[i]); + else if (parameters[i].ParameterType == typeof(bool)) + param[i] = ArgBool(args[i]); + } + + InvokeMethod(method, param); + } + }; + + commands[attr.Name] = info; + } + } + } + + private void InvokeMethod(MethodInfo method, object[] param = null) + { + try + { + method.Invoke(null, param); + } + catch (Exception e) + { + Engine.Commands.Log(e.InnerException.Message, Color.Yellow); + LogStackTrace(e.InnerException.StackTrace); + } + } + + private void LogStackTrace(string stackTrace) + { + foreach (var call in stackTrace.Split('\n')) + { + string log = call; + + //Remove File Path + { + var from = log.LastIndexOf(" in ") + 4; + var to = log.LastIndexOf('\\') + 1; + if (from != -1 && to != -1) + log = log.Substring(0, from) + log.Substring(to); + } + + //Remove arguments list + { + var from = log.IndexOf('(') + 1; + var to = log.IndexOf(')'); + if (from != -1 && to != -1) + log = log.Substring(0, from) + log.Substring(to); + } + + //Space out the colon line number + var colon = log.LastIndexOf(':'); + if (colon != -1) + log = log.Insert(colon + 1, " ").Insert(colon, " "); + + log = log.TrimStart(); + log = "-> " + log; + + Engine.Commands.Log(log, Color.White); + } + } + + private struct CommandInfo + { + public Action Action; + public string Help; + public string Usage; + } + + #region Parsing Arguments + + private static string ArgString(string arg) + { + if (arg == null) + return ""; + else + return arg; + } + + private static bool ArgBool(string arg) + { + if (arg != null) + return !(arg == "0" || arg.ToLower() == "false" || arg.ToLower() == "f"); + else + return false; + } + + private static int ArgInt(string arg) + { + try + { + return Convert.ToInt32(arg); + } + catch + { + return 0; + } + } + + private static float ArgFloat(string arg) + { + try + { + return Convert.ToSingle(arg); + } + catch + { + return 0; + } + } + + #endregion + + #endregion + + #region Built-In Commands +#if !CONSOLE + [Command("clear", "Clears the terminal")] + public static void Clear() + { + Engine.Commands.drawCommands.Clear(); + } + + [Command("exit", "Exits the game")] + private static void Exit() + { + Engine.Instance.Exit(); + } + + [Command("vsync", "Enables or disables vertical sync")] + private static void Vsync(bool enabled = true) + { + Engine.Graphics.SynchronizeWithVerticalRetrace = enabled; + Engine.Graphics.ApplyChanges(); + Engine.Commands.Log("Vertical Sync " + (enabled ? "Enabled" : "Disabled")); + } + + [Command("fixed", "Enables or disables fixed time step")] + private static void Fixed(bool enabled = true) + { + Engine.Instance.IsFixedTimeStep = enabled; + Engine.Commands.Log("Fixed Time Step " + (enabled ? "Enabled" : "Disabled")); + } + + [Command("framerate", "Sets the target framerate")] + private static void Framerate(float target) + { + Engine.Instance.TargetElapsedTime = TimeSpan.FromSeconds(1.0 / target); + } + + [Command("count", "Logs amount of Entities in the Scene. Pass a tagIndex to count only Entities with that tag")] + private static void Count(int tagIndex = -1) + { + if (Engine.Scene == null) + { + Engine.Commands.Log("Current Scene is null!"); + return; + } + + if (tagIndex < 0) + Engine.Commands.Log(Engine.Scene.Entities.Count.ToString()); + else + Engine.Commands.Log(Engine.Scene.TagLists[tagIndex].Count.ToString()); + } + + [Command("tracker", "Logs all tracked objects in the scene. Set mode to 'e' for just entities, 'c' for just components, or 'cc' for just collidable components")] + private static void Tracker(string mode) + { + if (Engine.Scene == null) + { + Engine.Commands.Log("Current Scene is null!"); + return; + } + + switch (mode) + { + default: + Engine.Commands.Log("-- Entities --"); + Engine.Scene.Tracker.LogEntities(); + Engine.Commands.Log("-- Components --"); + Engine.Scene.Tracker.LogComponents(); + Engine.Commands.Log("-- Collidable Components --"); + Engine.Scene.Tracker.LogCollidableComponents(); + break; + + case "e": + Engine.Scene.Tracker.LogEntities(); + break; + + case "c": + Engine.Scene.Tracker.LogComponents(); + break; + + case "cc": + Engine.Scene.Tracker.LogCollidableComponents(); + break; + } + } + + [Command("pooler", "Logs the pooled Entity counts")] + private static void Pooler() + { + Engine.Pooler.Log(); + } + + [Command("fullscreen", "Switches to fullscreen mode")] + private static void Fullscreen() + { + Engine.SetFullscreen(); + } + + [Command("window", "Switches to window mode")] + private static void Window(int scale = 1) + { + Engine.SetWindowed(Engine.Width * scale, Engine.Height * scale); + } + + [Command("help", "Shows usage help for a given command")] + private static void Help(string command) + { + if (Engine.Commands.sorted.Contains(command)) + { + var c = Engine.Commands.commands[command]; + StringBuilder str = new StringBuilder(); + + //Title + str.Append(":: "); + str.Append(command); + + //Usage + if (!string.IsNullOrEmpty(c.Usage)) + { + str.Append(" "); + str.Append(c.Usage); + } + Engine.Commands.Log(str.ToString()); + + //Help + if (string.IsNullOrEmpty(c.Help)) + Engine.Commands.Log("No help info set"); + else + Engine.Commands.Log(c.Help); + } + else + { + StringBuilder str = new StringBuilder(); + str.Append("Commands list: "); + str.Append(string.Join(", ", Engine.Commands.sorted)); + Engine.Commands.Log(str.ToString()); + Engine.Commands.Log("Type 'help command' for more info on that command!"); + } + } +#endif + #endregion + + private struct Line + { + public string Text; + public Color Color; + + public Line(string text) + { + Text = text; + Color = Color.White; + } + + public Line(string text, Color color) + { + Text = text; + Color = color; + } + } + } + + public class Command : Attribute + { + public string Name; + public string Help; + + public Command(string name, string help) + { + Name = name; + Help = help; + } + } +} + diff --git a/MonocleEngineDemo/Monocle/Util/Draw.cs b/MonocleEngineDemo/Monocle/Util/Draw.cs new file mode 100644 index 0000000..1a29e4e --- /dev/null +++ b/MonocleEngineDemo/Monocle/Util/Draw.cs @@ -0,0 +1,389 @@ +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using System; + +namespace Monocle +{ + public static class Draw + { + /// + /// The currently-rendering Renderer + /// + public static Renderer Renderer { get; internal set; } + + /// + /// All 2D rendering is done through this SpriteBatch instance + /// + public static SpriteBatch SpriteBatch { get; private set; } + + /// + /// The default Monocle font (Consolas 12). Loaded automatically by Monocle at startup + /// + public static SpriteFont DefaultFont { get; private set; } + + /// + /// A subtexture used to draw particle systems. + /// Will be generated at startup, but you can replace this with a subtexture from your Atlas to reduce texture swaps. + /// Should be a 2x2 white pixel + /// + public static MTexture Particle; + + /// + /// A subtexture used to draw rectangles and lines. + /// Will be generated at startup, but you can replace this with a subtexture from your Atlas to reduce texture swaps. + /// Use the top left pixel of your Particle Subtexture if you replace it! + /// Should be a 1x1 white pixel + /// + public static MTexture Pixel; + + private static Rectangle rect; + + internal static void Initialize(GraphicsDevice graphicsDevice) + { + SpriteBatch = new SpriteBatch(graphicsDevice); + DefaultFont = Engine.Instance.Content.Load(@"Monocle\MonocleDefault"); + UseDebugPixelTexture(); + } + + public static void UseDebugPixelTexture() + { + MTexture texture = new MTexture(2, 2, Color.White); + Pixel = new MTexture(texture, 0, 0, 1, 1); + Particle = new MTexture(texture, 0, 0, 2, 2); + } + + public static void Point(Vector2 at, Color color) + { + SpriteBatch.Draw(Pixel.Texture, at, Pixel.ClipRect, color, 0, Vector2.Zero, 1f, SpriteEffects.None, 0); + } + + #region Line + + public static void Line(Vector2 start, Vector2 end, Color color) + { + LineAngle(start, Calc.Angle(start, end), Vector2.Distance(start, end), color); + } + + public static void Line(Vector2 start, Vector2 end, Color color, float thickness) + { + LineAngle(start, Calc.Angle(start, end), Vector2.Distance(start, end), color, thickness); + } + + public static void Line(float x1, float y1, float x2, float y2, Color color) + { + Line(new Vector2(x1, y1), new Vector2(x2, y2), color); + } + + #endregion + + #region Line Angle + + public static void LineAngle(Vector2 start, float angle, float length, Color color) + { + SpriteBatch.Draw(Pixel.Texture, start, Pixel.ClipRect, color, angle, Vector2.Zero, new Vector2(length, 1), SpriteEffects.None, 0); + } + + public static void LineAngle(Vector2 start, float angle, float length, Color color, float thickness) + { + SpriteBatch.Draw(Pixel.Texture, start, Pixel.ClipRect, color, angle, new Vector2(0, .5f), new Vector2(length, thickness), SpriteEffects.None, 0); + } + + public static void LineAngle(float startX, float startY, float angle, float length, Color color) + { + LineAngle(new Vector2(startX, startY), angle, length, color); + } + + #endregion + + #region Circle + + public static void Circle(Vector2 position, float radius, Color color, int resolution) + { + Vector2 last = Vector2.UnitX * radius; + Vector2 lastP = last.Perpendicular(); + for (int i = 1; i <= resolution; i++) + { + Vector2 at = Calc.AngleToVector(i * MathHelper.PiOver2 / resolution, radius); + Vector2 atP = at.Perpendicular(); + + Draw.Line(position + last, position + at, color); + Draw.Line(position - last, position - at, color); + Draw.Line(position + lastP, position + atP, color); + Draw.Line(position - lastP, position - atP, color); + + last = at; + lastP = atP; + } + } + + public static void Circle(float x, float y, float radius, Color color, int resolution) + { + Circle(new Vector2(x, y), radius, color, resolution); + } + + public static void Circle(Vector2 position, float radius, Color color, float thickness, int resolution) + { + Vector2 last = Vector2.UnitX * radius; + Vector2 lastP = last.Perpendicular(); + for (int i = 1; i <= resolution; i++) + { + Vector2 at = Calc.AngleToVector(i * MathHelper.PiOver2 / resolution, radius); + Vector2 atP = at.Perpendicular(); + + Draw.Line(position + last, position + at, color, thickness); + Draw.Line(position - last, position - at, color, thickness); + Draw.Line(position + lastP, position + atP, color, thickness); + Draw.Line(position - lastP, position - atP, color, thickness); + + last = at; + lastP = atP; + } + } + + public static void Circle(float x, float y, float radius, Color color, float thickness, int resolution) + { + Circle(new Vector2(x, y), radius, color, thickness, resolution); + } + + #endregion + + #region Rect + + public static void Rect(float x, float y, float width, float height, Color color) + { + rect.X = (int)x; + rect.Y = (int)y; + rect.Width = (int)width; + rect.Height = (int)height; + SpriteBatch.Draw(Pixel.Texture, rect, Pixel.ClipRect, color); + } + + public static void Rect(Vector2 position, float width, float height, Color color) + { + Rect(position.X, position.Y, width, height, color); + } + + public static void Rect(Rectangle rect, Color color) + { + Draw.rect = rect; + SpriteBatch.Draw(Pixel.Texture, rect, Pixel.ClipRect, color); + } + + public static void Rect(Collider collider, Color color) + { + Rect(collider.AbsoluteLeft, collider.AbsoluteTop, collider.Width, collider.Height, color); + } + + #endregion + + #region Hollow Rect + + public static void HollowRect(float x, float y, float width, float height, Color color) + { + rect.X = (int)x; + rect.Y = (int)y; + rect.Width = (int)width; + rect.Height = 1; + + SpriteBatch.Draw(Pixel.Texture, rect, Pixel.ClipRect, color); + + rect.Y += (int)height - 1; + + SpriteBatch.Draw(Pixel.Texture, rect, Pixel.ClipRect, color); + + rect.Y -= (int)height - 1; + rect.Width = 1; + rect.Height = (int)height; + + SpriteBatch.Draw(Pixel.Texture, rect, Pixel.ClipRect, color); + + rect.X += (int)width - 1; + + SpriteBatch.Draw(Pixel.Texture, rect, Pixel.ClipRect, color); + } + + public static void HollowRect(Vector2 position, float width, float height, Color color) + { + HollowRect(position.X, position.Y, width, height, color); + } + + public static void HollowRect(Rectangle rect, Color color) + { + HollowRect(rect.X, rect.Y, rect.Width, rect.Height, color); + } + + public static void HollowRect(Collider collider, Color color) + { + HollowRect(collider.AbsoluteLeft, collider.AbsoluteTop, collider.Width, collider.Height, color); + } + + #endregion + + #region Text + + public static void Text(SpriteFont font, string text, Vector2 position, Color color) + { + Draw.SpriteBatch.DrawString(font, text, Calc.Floor(position), color); + } + + public static void Text(SpriteFont font, string text, Vector2 position, Color color, Vector2 origin, Vector2 scale, float rotation) + { + Draw.SpriteBatch.DrawString(font, text, Calc.Floor(position), color, rotation, origin, scale, SpriteEffects.None, 0); + } + + public static void TextJustified(SpriteFont font, string text, Vector2 position, Color color, Vector2 justify) + { + Vector2 origin = font.MeasureString(text); + origin.X *= justify.X; + origin.Y *= justify.Y; + + Draw.SpriteBatch.DrawString(font, text, Calc.Floor(position), color, 0, origin, 1, SpriteEffects.None, 0); + } + + public static void TextJustified(SpriteFont font, string text, Vector2 position, Color color, float scale, Vector2 justify) + { + Vector2 origin = font.MeasureString(text); + origin.X *= justify.X; + origin.Y *= justify.Y; + Draw.SpriteBatch.DrawString(font, text, Calc.Floor(position), color, 0, origin, scale, SpriteEffects.None, 0); + } + + public static void TextCentered(SpriteFont font, string text, Vector2 position) + { + Text(font, text, position - font.MeasureString(text) * .5f, Color.White); + } + + public static void TextCentered(SpriteFont font, string text, Vector2 position, Color color) + { + Text(font, text, position - font.MeasureString(text) * .5f, color); + } + + public static void TextCentered(SpriteFont font, string text, Vector2 position, Color color, float scale) + { + Text(font, text, position, color, font.MeasureString(text) * .5f, Vector2.One * scale, 0); + } + + public static void TextCentered(SpriteFont font, string text, Vector2 position, Color color, float scale, float rotation) + { + Text(font, text, position, color, font.MeasureString(text) * .5f, Vector2.One * scale, rotation); + } + + public static void OutlineTextCentered(SpriteFont font, string text, Vector2 position, Color color, float scale) + { + Vector2 origin = font.MeasureString(text) / 2; + + for (int i = -1; i < 2; i++) + for (int j = -1; j < 2; j++) + if (i != 0 || j != 0) + Draw.SpriteBatch.DrawString(font, text, Calc.Floor(position) + new Vector2(i, j), Color.Black, 0, origin, scale, SpriteEffects.None, 0); + Draw.SpriteBatch.DrawString(font, text, Calc.Floor(position), color, 0, origin, scale, SpriteEffects.None, 0); + } + + public static void OutlineTextCentered(SpriteFont font, string text, Vector2 position, Color color, Color outlineColor) + { + Vector2 origin = font.MeasureString(text) / 2; + + for (int i = -1; i < 2; i++) + for (int j = -1; j < 2; j++) + if (i != 0 || j != 0) + Draw.SpriteBatch.DrawString(font, text, Calc.Floor(position) + new Vector2(i, j), outlineColor, 0, origin, 1, SpriteEffects.None, 0); + Draw.SpriteBatch.DrawString(font, text, Calc.Floor(position), color, 0, origin, 1, SpriteEffects.None, 0); + } + + public static void OutlineTextCentered(SpriteFont font, string text, Vector2 position, Color color, Color outlineColor, float scale) + { + Vector2 origin = font.MeasureString(text) / 2; + + for (int i = -1; i < 2; i++) + for (int j = -1; j < 2; j++) + if (i != 0 || j != 0) + Draw.SpriteBatch.DrawString(font, text, Calc.Floor(position) + new Vector2(i, j), outlineColor, 0, origin, scale, SpriteEffects.None, 0); + Draw.SpriteBatch.DrawString(font, text, Calc.Floor(position), color, 0, origin, scale, SpriteEffects.None, 0); + } + + public static void OutlineTextJustify(SpriteFont font, string text, Vector2 position, Color color, Color outlineColor, Vector2 justify) + { + Vector2 origin = font.MeasureString(text) * justify; + + for (int i = -1; i < 2; i++) + for (int j = -1; j < 2; j++) + if (i != 0 || j != 0) + Draw.SpriteBatch.DrawString(font, text, Calc.Floor(position) + new Vector2(i, j), outlineColor, 0, origin, 1, SpriteEffects.None, 0); + Draw.SpriteBatch.DrawString(font, text, Calc.Floor(position), color, 0, origin, 1, SpriteEffects.None, 0); + } + + public static void OutlineTextJustify(SpriteFont font, string text, Vector2 position, Color color, Color outlineColor, Vector2 justify, float scale) + { + Vector2 origin = font.MeasureString(text) * justify; + + for (int i = -1; i < 2; i++) + for (int j = -1; j < 2; j++) + if (i != 0 || j != 0) + Draw.SpriteBatch.DrawString(font, text, Calc.Floor(position) + new Vector2(i, j), outlineColor, 0, origin, scale, SpriteEffects.None, 0); + Draw.SpriteBatch.DrawString(font, text, Calc.Floor(position), color, 0, origin, scale, SpriteEffects.None, 0); + } + + #endregion + + #region Weird Textures + + public static void SineTextureH(MTexture tex, Vector2 position, Vector2 origin, Vector2 scale, float rotation, Color color, SpriteEffects effects, float sineCounter, float amplitude = 2, int sliceSize = 2, float sliceAdd = MathHelper.TwoPi / 8) + { + position = Calc.Floor(position); + Rectangle clip = tex.ClipRect; + clip.Width = sliceSize; + + int num = 0; + while (clip.X < tex.ClipRect.X + tex.ClipRect.Width) + { + Vector2 add = new Vector2(sliceSize * num, (float)Math.Round(Math.Sin(sineCounter + sliceAdd * num) * amplitude)); + Draw.SpriteBatch.Draw(tex.Texture, position, clip, color, rotation, origin - add, scale, effects, 0); + + num++; + clip.X += sliceSize; + clip.Width = Math.Min(sliceSize, tex.ClipRect.X + tex.ClipRect.Width - clip.X); + } + } + + public static void SineTextureV(MTexture tex, Vector2 position, Vector2 origin, Vector2 scale, float rotation, Color color, SpriteEffects effects, float sineCounter, float amplitude = 2, int sliceSize = 2, float sliceAdd = MathHelper.TwoPi / 8) + { + position = Calc.Floor(position); + Rectangle clip = tex.ClipRect; + clip.Height = sliceSize; + + int num = 0; + while (clip.Y < tex.ClipRect.Y + tex.ClipRect.Height) + { + Vector2 add = new Vector2((float)Math.Round(Math.Sin(sineCounter + sliceAdd * num) * amplitude), sliceSize * num); + Draw.SpriteBatch.Draw(tex.Texture, position, clip, color, rotation, origin - add, scale, effects, 0); + + num++; + clip.Y += sliceSize; + clip.Height = Math.Min(sliceSize, tex.ClipRect.Y + tex.ClipRect.Height - clip.Y); + } + } + + public static void TextureBannerV(MTexture tex, Vector2 position, Vector2 origin, Vector2 scale, float rotation, Color color, SpriteEffects effects, float sineCounter, float amplitude = 2, int sliceSize = 2, float sliceAdd = MathHelper.TwoPi / 8) + { + position = Calc.Floor(position); + Rectangle clip = tex.ClipRect; + clip.Height = sliceSize; + + int num = 0; + while (clip.Y < tex.ClipRect.Y + tex.ClipRect.Height) + { + float fade = (clip.Y - tex.ClipRect.Y) / (float)tex.ClipRect.Height; + clip.Height = (int)MathHelper.Lerp(sliceSize, 1, fade); + clip.Height = Math.Min(sliceSize, tex.ClipRect.Y + tex.ClipRect.Height - clip.Y); + + Vector2 add = new Vector2((float)Math.Round(Math.Sin(sineCounter + sliceAdd * num) * amplitude * fade), clip.Y - tex.ClipRect.Y); + Draw.SpriteBatch.Draw(tex.Texture, position, clip, color, rotation, origin - add, scale, effects, 0); + + num++; + clip.Y += clip.Height; + } + } + + #endregion + } +} diff --git a/MonocleEngineDemo/Monocle/Util/Ease.cs b/MonocleEngineDemo/Monocle/Util/Ease.cs new file mode 100644 index 0000000..7890fac --- /dev/null +++ b/MonocleEngineDemo/Monocle/Util/Ease.cs @@ -0,0 +1,125 @@ +using Microsoft.Xna.Framework; +using System; + +namespace Monocle +{ + public static class Ease + { + public delegate float Easer(float t); + + public static readonly Easer Linear = (float t) => { return t; }; + + public static readonly Easer SineIn = (float t) => { return -(float)Math.Cos(MathHelper.PiOver2 * t) + 1; }; + public static readonly Easer SineOut = (float t) => { return (float)Math.Sin(MathHelper.PiOver2 * t); }; + public static readonly Easer SineInOut = (float t) => { return -(float)Math.Cos(MathHelper.Pi * t) / 2f + .5f; }; + + public static readonly Easer QuadIn = (float t) => { return t * t; }; + public static readonly Easer QuadOut = Invert(QuadIn); + public static readonly Easer QuadInOut = Follow(QuadIn, QuadOut); + + public static readonly Easer CubeIn = (float t) => { return t * t * t; }; + public static readonly Easer CubeOut = Invert(CubeIn); + public static readonly Easer CubeInOut = Follow(CubeIn, CubeOut); + + public static readonly Easer QuintIn = (float t) => { return t * t * t * t * t; }; + public static readonly Easer QuintOut = Invert(QuintIn); + public static readonly Easer QuintInOut = Follow(QuintIn, QuintOut); + + public static readonly Easer ExpoIn = (float t) => { return (float)Math.Pow(2, 10 * (t - 1)); }; + public static readonly Easer ExpoOut = Invert(ExpoIn); + public static readonly Easer ExpoInOut = Follow(ExpoIn, ExpoOut); + + public static readonly Easer BackIn = (float t) => { return t * t * (2.70158f * t - 1.70158f); }; + public static readonly Easer BackOut = Invert(BackIn); + public static readonly Easer BackInOut = Follow(BackIn, BackOut); + + public static readonly Easer BigBackIn = (float t) => { return t * t * (4f * t - 3f); }; + public static readonly Easer BigBackOut = Invert(BigBackIn); + public static readonly Easer BigBackInOut = Follow(BigBackIn, BigBackOut); + + public static readonly Easer ElasticIn = (float t) => + { + var ts = t * t; + var tc = ts * t; + return (33 * tc * ts + -59 * ts * ts + 32 * tc + -5 * ts); + }; + public static readonly Easer ElasticOut = (float t) => + { + var ts = t * t; + var tc = ts * t; + return (33 * tc * ts + -106 * ts * ts + 126 * tc + -67 * ts + 15 * t); + }; + public static readonly Easer ElasticInOut = Follow(ElasticIn, ElasticOut); + + private const float B1 = 1f / 2.75f; + private const float B2 = 2f / 2.75f; + private const float B3 = 1.5f / 2.75f; + private const float B4 = 2.5f / 2.75f; + private const float B5 = 2.25f / 2.75f; + private const float B6 = 2.625f / 2.75f; + + public static readonly Easer BounceIn = (float t) => + { + t = 1 - t; + if (t < B1) + return 1 - 7.5625f * t * t; + if (t < B2) + return 1 - (7.5625f * (t - B3) * (t - B3) + .75f); + if (t < B4) + return 1 - (7.5625f * (t - B5) * (t - B5) + .9375f); + return 1 - (7.5625f * (t - B6) * (t - B6) + .984375f); + }; + + public static readonly Easer BounceOut = (float t) => + { + if (t < B1) + return 7.5625f * t * t; + if (t < B2) + return 7.5625f * (t - B3) * (t - B3) + .75f; + if (t < B4) + return 7.5625f * (t - B5) * (t - B5) + .9375f; + return 7.5625f * (t - B6) * (t - B6) + .984375f; + }; + + public static readonly Easer BounceInOut = (float t) => + { + if (t < .5f) + { + t = 1 - t * 2; + if (t < B1) + return (1 - 7.5625f * t * t) / 2; + if (t < B2) + return (1 - (7.5625f * (t - B3) * (t - B3) + .75f)) / 2; + if (t < B4) + return (1 - (7.5625f * (t - B5) * (t - B5) + .9375f)) / 2; + return (1 - (7.5625f * (t - B6) * (t - B6) + .984375f)) / 2; + } + t = t * 2 - 1; + if (t < B1) + return (7.5625f * t * t) / 2 + .5f; + if (t < B2) + return (7.5625f * (t - B3) * (t - B3) + .75f) / 2 + .5f; + if (t < B4) + return (7.5625f * (t - B5) * (t - B5) + .9375f) / 2 + .5f; + return (7.5625f * (t - B6) * (t - B6) + .984375f) / 2 + .5f; + }; + + public static Easer Invert(Easer easer) + { + return (float t) => { return 1 - easer(1 - t); }; + } + + public static Easer Follow(Easer first, Easer second) + { + return (float t) => { return (t <= 0.5f) ? first(t * 2) / 2 : second(t * 2 - 1) / 2 + 0.5f; }; + } + + public static float UpDown(float eased) + { + if (eased <= .5f) + return eased * 2; + else + return 1 - (eased - .5f) * 2; + } + } +} diff --git a/MonocleEngineDemo/Monocle/Util/ErrorLog.cs b/MonocleEngineDemo/Monocle/Util/ErrorLog.cs new file mode 100644 index 0000000..6b08566 --- /dev/null +++ b/MonocleEngineDemo/Monocle/Util/ErrorLog.cs @@ -0,0 +1,78 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Monocle +{ + public static class ErrorLog + { + public const string Filename = "error_log.txt"; + public const string Marker = "=========================================="; + + public static void Write(Exception e) + { + Write(e.ToString()); + } + + public static void Write(string str) + { + StringBuilder s = new StringBuilder(); + + //Get the previous contents + string content = ""; + if (File.Exists(Filename)) + { + TextReader tr = new StreamReader(Filename); + content = tr.ReadToEnd(); + tr.Close(); + + if (!content.Contains(Marker)) + content = ""; + } + + //Header + if (Engine.Instance != null) + s.Append(Engine.Instance.Title); + else + s.Append("Monocle Engine"); + s.AppendLine(" Error Log"); + s.AppendLine(Marker); + s.AppendLine(); + + //Version Number + if (Engine.Instance.Version != null) + { + s.Append("Ver "); + s.AppendLine(Engine.Instance.Version.ToString()); + } + + //Datetime + s.AppendLine(DateTime.Now.ToString()); + + //String + s.AppendLine(str); + + //If the file wasn't empty, preserve the old errors + if (content != "") + { + int at = content.IndexOf(Marker) + Marker.Length; + string after = content.Substring(at); + s.AppendLine(after); + } + + TextWriter tw = new StreamWriter(Filename, false); + tw.Write(s.ToString()); + tw.Close(); + } + + public static void Open() + { + if (File.Exists(Filename)) + System.Diagnostics.Process.Start(Filename); + } + } +} diff --git a/MonocleEngineDemo/Monocle/Util/MethodHandle.cs b/MonocleEngineDemo/Monocle/Util/MethodHandle.cs new file mode 100644 index 0000000..10c4963 --- /dev/null +++ b/MonocleEngineDemo/Monocle/Util/MethodHandle.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; + +namespace Monocle +{ + public class MethodHandle where T : Entity + { + private MethodInfo info; + + public MethodHandle(string methodName) + { + info = typeof(T).GetMethod(methodName, BindingFlags.Public | BindingFlags.NonPublic); + } + + public void Call(T instance) + { + info.Invoke(instance, null); + } + } +} diff --git a/MonocleEngineDemo/Monocle/Util/PixelFont.cs b/MonocleEngineDemo/Monocle/Util/PixelFont.cs new file mode 100644 index 0000000..b0ec07e --- /dev/null +++ b/MonocleEngineDemo/Monocle/Util/PixelFont.cs @@ -0,0 +1,430 @@ +using Microsoft.Xna.Framework; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Xml; +using System.Text.RegularExpressions; +using System.IO; + +namespace Monocle +{ + public class PixelFontCharacter + { + public int Character; + public MTexture Texture; + public int XOffset; + public int YOffset; + public int XAdvance; + public Dictionary Kerning = new Dictionary(); + + public PixelFontCharacter(int character, MTexture texture, XmlElement xml) + { + Character = character; + Texture = texture.GetSubtexture(xml.AttrInt("x"), xml.AttrInt("y"), xml.AttrInt("width"), xml.AttrInt("height")); + XOffset = xml.AttrInt("xoffset"); + YOffset = xml.AttrInt("yoffset"); + XAdvance = xml.AttrInt("xadvance"); + } + } + + public class PixelFontSize + { + public List Textures; + public Dictionary Characters; + public int LineHeight; + public float Size; + public bool Outline; + + private StringBuilder temp = new StringBuilder(); + + public string AutoNewline(string text, int width) + { + if (string.IsNullOrEmpty(text)) + return text; + + temp.Clear(); + + var words = Regex.Split(text, @"(\s)"); + var lineWidth = 0f; + + foreach (var word in words) + { + var wordWidth = Measure(word).X; + if (wordWidth + lineWidth > width) + { + temp.Append('\n'); + lineWidth = 0; + + if (word.Equals(" ")) + continue; + } + + // this word is longer than the max-width, split where ever we can + if (wordWidth > width) + { + int i = 1, start = 0; + for (; i < word.Length; i++) + if (i - start > 1 && Measure(word.Substring(start, i - start - 1)).X > width) + { + temp.Append(word.Substring(start, i - start - 1)); + temp.Append('\n'); + start = i - 1; + } + + + var remaining = word.Substring(start, word.Length - start); + temp.Append(remaining); + lineWidth += Measure(remaining).X; + } + // normal word, add it + else + { + lineWidth += wordWidth; + temp.Append(word); + } + } + + return temp.ToString(); + } + + public PixelFontCharacter Get(int id) + { + PixelFontCharacter val = null; + if (Characters.TryGetValue(id, out val)) + return val; + return null; + } + + public Vector2 Measure(char text) + { + PixelFontCharacter c = null; + if (Characters.TryGetValue(text, out c)) + return new Vector2(c.XAdvance, LineHeight); + return Vector2.Zero; + } + + public Vector2 Measure(string text) + { + if (string.IsNullOrEmpty(text)) + return Vector2.Zero; + + var size = new Vector2(0, LineHeight); + var currentLineWidth = 0f; + + for (var i = 0; i < text.Length; i++) + { + if (text[i] == '\n') + { + size.Y += LineHeight; + if (currentLineWidth > size.X) + size.X = currentLineWidth; + currentLineWidth = 0f; + } + else + { + PixelFontCharacter c = null; + if (Characters.TryGetValue(text[i], out c)) + { + currentLineWidth += c.XAdvance; + + int kerning; + if (i < text.Length - 1 && c.Kerning.TryGetValue(text[i + 1], out kerning)) + currentLineWidth += kerning; + } + } + } + + if (currentLineWidth > size.X) + size.X = currentLineWidth; + + return size; + } + + public float WidthToNextLine(string text, int start) + { + if (string.IsNullOrEmpty(text)) + return 0; + + var currentLineWidth = 0f; + + for (int i = start, j = text.Length; i < j; i++) + { + if (text[i] == '\n') + break; + + PixelFontCharacter c = null; + if (Characters.TryGetValue(text[i], out c)) + { + currentLineWidth += c.XAdvance; + + int kerning; + if (i < j - 1 && c.Kerning.TryGetValue(text[i + 1], out kerning)) + currentLineWidth += kerning; + } + } + + return currentLineWidth; + } + + public float HeightOf(string text) + { + if (string.IsNullOrEmpty(text)) + return 0; + + int lines = 1; + if (text.IndexOf('\n') >= 0) + for (int i = 0; i < text.Length; i++) + if (text[i] == '\n') + lines++; + return lines * LineHeight; + } + + public void Draw(char character, Vector2 position, Vector2 justify, Vector2 scale, Color color) + { + if (char.IsWhiteSpace(character)) + return; + + PixelFontCharacter c = null; + if (Characters.TryGetValue(character, out c)) + { + var measure = Measure(character); + var justified = new Vector2(measure.X * justify.X, measure.Y * justify.Y); + var pos = position + (new Vector2(c.XOffset, c.YOffset) - justified) * scale; + c.Texture.Draw(Calc.Floor(pos), Vector2.Zero, color, scale); + } + } + + public void Draw(string text, Vector2 position, Vector2 justify, Vector2 scale, Color color, float edgeDepth, Color edgeColor, float stroke, Color strokeColor) + { + if (string.IsNullOrEmpty(text)) + return; + + var offset = Vector2.Zero; + var lineWidth = (justify.X != 0 ? WidthToNextLine(text, 0) : 0); + var justified = new Vector2(lineWidth * justify.X, HeightOf(text) * justify.Y); + + for (int i = 0; i < text.Length; i++) + { + if (text[i] == '\n') + { + offset.X = 0; + offset.Y += LineHeight; + if (justify.X != 0) + justified.X = WidthToNextLine(text, i + 1) * justify.X; + continue; + } + + PixelFontCharacter c = null; + if (Characters.TryGetValue(text[i], out c)) + { + var pos = (position + (offset + new Vector2(c.XOffset, c.YOffset) - justified) * scale); + + // draw stroke + if (stroke > 0 && !Outline) + { + if (edgeDepth > 0) + { + c.Texture.Draw(pos + new Vector2(0, -stroke), Vector2.Zero, strokeColor, scale); + for (var j = -stroke; j < edgeDepth + stroke; j += stroke) + { + c.Texture.Draw(pos + new Vector2(-stroke, j), Vector2.Zero, strokeColor, scale); + c.Texture.Draw(pos + new Vector2(stroke, j), Vector2.Zero, strokeColor, scale); + } + c.Texture.Draw(pos + new Vector2(-stroke, edgeDepth + stroke), Vector2.Zero, strokeColor, scale); + c.Texture.Draw(pos + new Vector2(0, edgeDepth + stroke), Vector2.Zero, strokeColor, scale); + c.Texture.Draw(pos + new Vector2(stroke, edgeDepth + stroke), Vector2.Zero, strokeColor, scale); + } + else + { + c.Texture.Draw(pos + new Vector2(-1, -1) * stroke, Vector2.Zero, strokeColor, scale); + c.Texture.Draw(pos + new Vector2(0, -1) * stroke, Vector2.Zero, strokeColor, scale); + c.Texture.Draw(pos + new Vector2(1, -1) * stroke, Vector2.Zero, strokeColor, scale); + c.Texture.Draw(pos + new Vector2(-1, 0) * stroke, Vector2.Zero, strokeColor, scale); + c.Texture.Draw(pos + new Vector2(1, 0) * stroke, Vector2.Zero, strokeColor, scale); + c.Texture.Draw(pos + new Vector2(-1, 1) * stroke, Vector2.Zero, strokeColor, scale); + c.Texture.Draw(pos + new Vector2(0, 1) * stroke, Vector2.Zero, strokeColor, scale); + c.Texture.Draw(pos + new Vector2(1, 1) * stroke, Vector2.Zero, strokeColor, scale); + } + } + + // draw edge + if (edgeDepth > 0) + c.Texture.Draw(pos + Vector2.UnitY * edgeDepth, Vector2.Zero, edgeColor, scale); + + // draw normal character + c.Texture.Draw(pos, Vector2.Zero, color, scale); + + offset.X += c.XAdvance; + + int kerning; + if (i < text.Length - 1 && c.Kerning.TryGetValue(text[i + 1], out kerning)) + offset.X += kerning; + } + } + + } + + public void Draw(string text, Vector2 position, Color color) + { + Draw(text, position, Vector2.Zero, Vector2.One, color, 0, Color.Transparent, 0, Color.Transparent); + } + + public void Draw(string text, Vector2 position, Vector2 justify, Vector2 scale, Color color) + { + Draw(text, position, justify, scale, color, 0, Color.Transparent, 0, Color.Transparent); + } + + public void DrawOutline(string text, Vector2 position, Vector2 justify, Vector2 scale, Color color, float stroke, Color strokeColor) + { + Draw(text, position, justify, scale, color, 0f, Color.Transparent, stroke, strokeColor); + } + + public void DrawEdgeOutline(string text, Vector2 position, Vector2 justify, Vector2 scale, Color color, float edgeDepth, Color edgeColor, float stroke = 0f, Color strokeColor = default(Color)) + { + Draw(text, position, justify, scale, color, edgeDepth, edgeColor, stroke, strokeColor); + } + } + + public class PixelFont + { + public string Face; + public List Sizes = new List(); + public List Textures; + + public PixelFont(string face) + { + Face = face; + } + + public PixelFontSize AddFontSize(string path, Atlas atlas = null, bool outline = false) + { + var data = Calc.LoadXML(path)["font"]; + return AddFontSize(path, data, atlas, outline); + } + + public PixelFontSize AddFontSize(string path, XmlElement data, Atlas atlas = null, bool outline = false) + { + // check if size already exists + var size = data["info"].AttrFloat("size"); + foreach (var fs in Sizes) + if (fs.Size == size) + return fs; + + // get textures + Textures = new List(); + var pages = data["pages"]; + foreach (XmlElement page in pages) + { + var file = page.Attr("file"); + var atlasPath = Path.GetFileNameWithoutExtension(file); + + if (atlas != null && atlas.Has(atlasPath)) + { + Textures.Add(atlas[atlasPath]); + } + else + { + var dir = Path.GetDirectoryName(path); + dir = dir.Substring(Engine.ContentDirectory.Length + 1); + Textures.Add(MTexture.FromFile(Path.Combine(dir, file))); + } + } + + // create font size + var fontSize = new PixelFontSize() + { + Textures = Textures, + Characters = new Dictionary(), + LineHeight = data["common"].AttrInt("lineHeight"), + Size = size, + Outline = outline + }; + + // get characters + foreach (XmlElement character in data["chars"]) + { + int id = character.AttrInt("id"); + int page = character.AttrInt("page", 0); + fontSize.Characters.Add(id, new PixelFontCharacter(id, Textures[page], character)); + } + + // get kerning + if (data["kernings"] != null) + foreach (XmlElement kerning in data["kernings"]) + { + var from = kerning.AttrInt("first"); + var to = kerning.AttrInt("second"); + var push = kerning.AttrInt("amount"); + + PixelFontCharacter c = null; + if (fontSize.Characters.TryGetValue(from, out c)) + c.Kerning.Add(to, push); + } + + // add font size + Sizes.Add(fontSize); + Sizes.Sort((a, b) => { return Math.Sign(a.Size - b.Size); }); + + return fontSize; + } + + public PixelFontSize Get(float size) + { + for (int i = 0, j = Sizes.Count - 1; i < j; i++) + if (Sizes[i].Size >= size) + return Sizes[i]; + return Sizes[Sizes.Count - 1]; + } + + public void Draw(float baseSize, char character, Vector2 position, Vector2 justify, Vector2 scale, Color color) + { + var fontSize = Get(baseSize * Math.Max(scale.X, scale.Y)); + scale *= (baseSize / fontSize.Size); + fontSize.Draw(character, position, justify, scale, color); + } + + public void Draw(float baseSize, string text, Vector2 position, Vector2 justify, Vector2 scale, Color color, float edgeDepth, Color edgeColor, float stroke, Color strokeColor) + { + var fontSize = Get(baseSize * Math.Max(scale.X, scale.Y)); + scale *= (baseSize / fontSize.Size); + fontSize.Draw(text, position, justify, scale, color, edgeDepth, edgeColor, stroke, strokeColor); + } + + public void Draw(float baseSize, string text, Vector2 position, Color color) + { + var scale = Vector2.One; + var fontSize = Get(baseSize * Math.Max(scale.X, scale.Y)); + scale *= (baseSize / fontSize.Size); + fontSize.Draw(text, position, Vector2.Zero, Vector2.One, color, 0, Color.Transparent, 0, Color.Transparent); + } + + public void Draw(float baseSize, string text, Vector2 position, Vector2 justify, Vector2 scale, Color color) + { + var fontSize = Get(baseSize * Math.Max(scale.X, scale.Y)); + scale *= (baseSize / fontSize.Size); + fontSize.Draw(text, position, justify, scale, color, 0, Color.Transparent, 0, Color.Transparent); + } + + public void DrawOutline(float baseSize, string text, Vector2 position, Vector2 justify, Vector2 scale, Color color, float stroke, Color strokeColor) + { + var fontSize = Get(baseSize * Math.Max(scale.X, scale.Y)); + scale *= (baseSize / fontSize.Size); + fontSize.Draw(text, position, justify, scale, color, 0f, Color.Transparent, stroke, strokeColor); + } + + public void DrawEdgeOutline(float baseSize, string text, Vector2 position, Vector2 justify, Vector2 scale, Color color, float edgeDepth, Color edgeColor, float stroke = 0f, Color strokeColor = default(Color)) + { + var fontSize = Get(baseSize * Math.Max(scale.X, scale.Y)); + scale *= (baseSize / fontSize.Size); + fontSize.Draw(text, position, justify, scale, color, edgeDepth, edgeColor, stroke, strokeColor); + } + + public void Dispose() + { + foreach (var tex in Textures) + tex.Dispose(); + Sizes.Clear(); + } + } +} diff --git a/MonocleEngineDemo/Monocle/Util/Pnt.cs b/MonocleEngineDemo/Monocle/Util/Pnt.cs new file mode 100644 index 0000000..124093c --- /dev/null +++ b/MonocleEngineDemo/Monocle/Util/Pnt.cs @@ -0,0 +1,118 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Monocle +{ + public struct Pnt + { + public static readonly Pnt Zero = new Pnt(0, 0); + public static readonly Pnt UnitX = new Pnt(1, 0); + public static readonly Pnt UnitY = new Pnt(0, 1); + public static readonly Pnt One = new Pnt(1, 1); + + public int X; + public int Y; + + public Pnt(int x, int y) + { + X = x; + Y = y; + } + + #region Pnt operators + + public static bool operator ==(Pnt a, Pnt b) + { + return a.X == b.X && a.Y == b.Y; + } + + public static bool operator !=(Pnt a, Pnt b) + { + return a.X != b.X || a.Y != b.Y; + } + + public static Pnt operator +(Pnt a, Pnt b) + { + return new Pnt(a.X + b.X, a.Y + b.Y); + } + + public static Pnt operator -(Pnt a, Pnt b) + { + return new Pnt(a.X - b.X, a.Y - b.Y); + } + + public static Pnt operator *(Pnt a, Pnt b) + { + return new Pnt(a.X * b.X, a.Y * b.Y); + } + + public static Pnt operator /(Pnt a, Pnt b) + { + return new Pnt(a.X / b.X, a.Y / b.Y); + } + + public static Pnt operator %(Pnt a, Pnt b) + { + return new Pnt(a.X % b.X, a.Y % b.Y); + } + + #endregion + + #region int operators + + public static bool operator ==(Pnt a, int b) + { + return a.X == b && a.Y == b; + } + + public static bool operator !=(Pnt a, int b) + { + return a.X != b || a.Y != b; + } + + public static Pnt operator +(Pnt a, int b) + { + return new Pnt(a.X + b, a.Y + b); + } + + public static Pnt operator -(Pnt a, int b) + { + return new Pnt(a.X - b, a.Y - b); + } + + public static Pnt operator *(Pnt a, int b) + { + return new Pnt(a.X * b, a.Y * b); + } + + public static Pnt operator /(Pnt a, int b) + { + return new Pnt(a.X / b, a.Y / b); + } + + public static Pnt operator %(Pnt a, int b) + { + return new Pnt(a.X % b, a.Y % b); + } + + #endregion + + public override bool Equals(object obj) + { + return false; + } + + public override int GetHashCode() + { + return X * 10000 + Y; + } + + public override string ToString() + { + return "{ X: " + X + ", Y: " + Y + " }"; + } + } +} diff --git a/MonocleEngineDemo/Monocle/Util/Pooler.cs b/MonocleEngineDemo/Monocle/Util/Pooler.cs new file mode 100644 index 0000000..e13fe38 --- /dev/null +++ b/MonocleEngineDemo/Monocle/Util/Pooler.cs @@ -0,0 +1,67 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; + +namespace Monocle +{ + public class Pooler + { + internal Dictionary> Pools { get; private set; } + + public Pooler() + { + Pools = new Dictionary>(); + + foreach (var type in Assembly.GetEntryAssembly().GetTypes()) + { + if (type.GetCustomAttributes(typeof(Pooled), false).Length > 0) + { + if (!typeof(Entity).IsAssignableFrom(type)) + throw new Exception("Type '" + type.Name + "' cannot be Pooled because it doesn't derive from Entity"); + else if (type.GetConstructor(Type.EmptyTypes) == null) + throw new Exception("Type '" + type.Name + "' cannot be Pooled because it doesn't have a parameterless constructor"); + else + Pools.Add(type, new Queue()); + } + } + } + + public T Create() where T : Entity, new() + { + if (!Pools.ContainsKey(typeof(T))) + return new T(); + + var queue = Pools[typeof(T)]; + if (queue.Count == 0) + return new T(); + else + return queue.Dequeue() as T; + } + + internal void EntityRemoved(Entity entity) + { + var type = entity.GetType(); + if (Pools.ContainsKey(type)) + Pools[type].Enqueue(entity); + } + + public void Log() + { + if (Pools.Count == 0) + Engine.Commands.Log("No Entity types are marked as Pooled!"); + + foreach (var kv in Pools) + { + string output = kv.Key.Name + " : " + kv.Value.Count; + Engine.Commands.Log(output); + } + } + } + + public class Pooled : Attribute + { + + } +} diff --git a/MonocleEngineDemo/Monocle/Util/SaveLoad.cs b/MonocleEngineDemo/Monocle/Util/SaveLoad.cs new file mode 100644 index 0000000..3be9f40 --- /dev/null +++ b/MonocleEngineDemo/Monocle/Util/SaveLoad.cs @@ -0,0 +1,150 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.Serialization; +using System.Runtime.Serialization.Formatters.Binary; +using System.Text; +using System.Threading.Tasks; +using System.Xml.Serialization; + +namespace Monocle +{ + public static class SaveLoad + { + public enum SerializeModes { Binary, XML }; + + #region Save + + /// + /// Save an object to a file so you can load it later + /// + public static void SerializeToFile(T obj, string filepath, SerializeModes mode) + { + using (var fileStream = new FileStream(filepath, FileMode.Create)) + { + //Serialize + if (mode == SerializeModes.Binary) + { + var bf = new BinaryFormatter(); + bf.Serialize(fileStream, obj); + } + else if (mode == SerializeModes.XML) + { + var xs = new XmlSerializer(typeof(T)); + xs.Serialize(fileStream, obj); + } + } + } + + /// + /// Save an object to a file so you can load it later. + /// Will not crash if the save fails + /// + /// Whether the save succeeded + public static bool SafeSerializeToFile(T obj, string filepath, SerializeModes mode) + { + try + { + SerializeToFile(obj, filepath, mode); + return true; + } + catch + { + return false; + } + } + + #endregion + + #region Load + + /// + /// Load an object that was previously serialized to a file + /// + public static T DeserializeFromFile(string filepath, SerializeModes mode) + { + T data; + using (var fileStream = File.OpenRead(filepath)) + { + + //Deserialize + if (mode == SerializeModes.Binary) + { + var bf = new BinaryFormatter(); + data = (T)bf.Deserialize(fileStream); + } + else + { + var xs = new XmlSerializer(typeof(T)); + data = (T)xs.Deserialize(fileStream); + } + } + + return data; + } + + /// + /// Load an object that was previously serialized to a file + /// If the load fails or the file does not exist, default(T) will be returned + /// + public static T SafeDeserializeFromFile(string filepath, SerializeModes mode, bool debugUnsafe = false) + { + if (File.Exists(filepath)) + { + if (debugUnsafe) + return SaveLoad.DeserializeFromFile(filepath, mode); + else + { + try + { + return SaveLoad.DeserializeFromFile(filepath, mode); + } + catch + { + return default(T); + } + } + } + else + return default(T); + } + + /// + /// Load an object that was previously serialized to a file + /// If the load fails or the file does not exist, default(T) will be returned + /// + /// True if the load fails despite the requested file existing (for example due to corrupted data) + public static T SafeDeserializeFromFile(string filepath, SerializeModes mode, out bool loadError, bool debugUnsafe = false) + { + if (File.Exists(filepath)) + { + if (debugUnsafe) + { + loadError = false; + return SaveLoad.DeserializeFromFile(filepath, mode); + } + else + { + try + { + loadError = false; + return SaveLoad.DeserializeFromFile(filepath, mode); + } + catch + { + loadError = true; + return default(T); + } + } + } + else + { + loadError = false; + return default(T); + } + } + + #endregion + } +} diff --git a/MonocleEngineDemo/Monocle/Util/SimpleCurve.cs b/MonocleEngineDemo/Monocle/Util/SimpleCurve.cs new file mode 100644 index 0000000..f4acf2d --- /dev/null +++ b/MonocleEngineDemo/Monocle/Util/SimpleCurve.cs @@ -0,0 +1,78 @@ +using Microsoft.Xna.Framework; + +namespace Monocle +{ + public struct SimpleCurve + { + public Vector2 Begin; + public Vector2 End; + public Vector2 Control; + + public SimpleCurve(Vector2 begin, Vector2 end, Vector2 control) + { + Begin = begin; + End = end; + Control = control; + } + + public void DoubleControl() + { + Vector2 axis = End - Begin; + Vector2 mid = Begin + axis/2; + Vector2 diff = Control - mid; + Control += diff; + } + + public Vector2 GetPoint(float percent) + { + float reverse = 1 - percent; + return (reverse * reverse * Begin) + (2f * reverse * percent * Control) + (percent * percent * End); + } + + public float GetLengthParametric(int resolution) + { + Vector2 last = Begin; + float length = 0; + for (int i = 1; i <= resolution; i++) + { + Vector2 at = GetPoint(i / (float)resolution); + length += (at - last).Length(); + last = at; + } + + return length; + } + + public void Render(Vector2 offset, Color color, int resolution) + { + Vector2 last = offset + Begin; + for (int i = 1; i <= resolution; i++) + { + Vector2 at = offset + GetPoint(i / (float)resolution); + Draw.Line(last, at, color); + last = at; + } + } + + public void Render(Vector2 offset, Color color, int resolution, float thickness) + { + Vector2 last = offset + Begin; + for (int i = 1; i <= resolution; i++) + { + Vector2 at = offset + GetPoint(i / (float)resolution); + Draw.Line(last, at, color, thickness); + last = at; + } + } + + public void Render(Color color, int resolution) + { + Render(Vector2.Zero, color, resolution); + } + + public void Render(Color color, int resolution, float thickness) + { + Render(Vector2.Zero, color, resolution, thickness); + } + } +} diff --git a/MonocleEngineDemo/Monocle/Util/SpecEntity.cs b/MonocleEngineDemo/Monocle/Util/SpecEntity.cs new file mode 100644 index 0000000..81443d7 --- /dev/null +++ b/MonocleEngineDemo/Monocle/Util/SpecEntity.cs @@ -0,0 +1,34 @@ +using Microsoft.Xna.Framework; + +namespace Monocle +{ + public class SpecEntity : Entity where T : Scene + { + public T SpecScene { get; private set; } + + public SpecEntity(Vector2 position) + : base(position) + { + + } + + public SpecEntity() + : base() + { + + } + + public override void Added(Scene scene) + { + base.Added(scene); + if (Scene is T) + SpecScene = Scene as T; + } + + public override void Removed(Scene scene) + { + SpecScene = null; + base.Removed(scene); + } + } +} diff --git a/MonocleEngineDemo/Monocle/Util/Tiler.cs b/MonocleEngineDemo/Monocle/Util/Tiler.cs new file mode 100644 index 0000000..e54ef1f --- /dev/null +++ b/MonocleEngineDemo/Monocle/Util/Tiler.cs @@ -0,0 +1,275 @@ +using System; +using System.Xml; + +namespace Monocle +{ + public static class Tiler + { + public enum EdgeBehavior { True, False, Wrap }; + + public static int[,] Tile(bool[,] bits, Func tileDecider, Action tileOutput, int tileWidth, int tileHeight, EdgeBehavior edges) + { + int boundsX = bits.GetLength(0); + int boundsY = bits.GetLength(1); + int[,] tiles = new int[boundsX, boundsY]; + + for (TileX = 0; TileX < boundsX; TileX++) + { + for (TileY = 0; TileY < boundsY; TileY++) + { + if (bits[TileX, TileY]) + { + switch (edges) + { + case EdgeBehavior.True: + Left = TileX == 0 ? true : bits[TileX - 1, TileY]; + Right = TileX == boundsX - 1 ? true : bits[TileX + 1, TileY]; + Up = TileY == 0 ? true : bits[TileX, TileY - 1]; + Down = TileY == boundsY - 1 ? true : bits[TileX, TileY + 1]; + + UpLeft = (TileX == 0 || TileY == 0) ? true : bits[TileX - 1, TileY - 1]; + UpRight = (TileX == boundsX - 1 || TileY == 0) ? true : bits[TileX + 1, TileY - 1]; + DownLeft = (TileX == 0 || TileY == boundsY - 1) ? true : bits[TileX - 1, TileY + 1]; + DownRight = (TileX == boundsX - 1 || TileY == boundsY - 1) ? true : bits[TileX + 1, TileY + 1]; + break; + + case EdgeBehavior.False: + Left = TileX == 0 ? false : bits[TileX - 1, TileY]; + Right = TileX == boundsX - 1 ? false : bits[TileX + 1, TileY]; + Up = TileY == 0 ? false : bits[TileX, TileY - 1]; + Down = TileY == boundsY - 1 ? false : bits[TileX, TileY + 1]; + + UpLeft = (TileX == 0 || TileY == 0) ? false : bits[TileX - 1, TileY - 1]; + UpRight = (TileX == boundsX - 1 || TileY == 0) ? false : bits[TileX + 1, TileY - 1]; + DownLeft = (TileX == 0 || TileY == boundsY - 1) ? false : bits[TileX - 1, TileY + 1]; + DownRight = (TileX == boundsX - 1 || TileY == boundsY - 1) ? false : bits[TileX + 1, TileY + 1]; + break; + + case EdgeBehavior.Wrap: + Left = bits[(TileX + boundsX - 1) % boundsX, TileY]; + Right = bits[(TileX + 1) % boundsX, TileY]; + Up = bits[TileX, (TileY + boundsY - 1) % boundsY]; + Down = bits[TileX, (TileY + 1) % boundsY]; + + UpLeft = bits[(TileX + boundsX - 1) % boundsX, (TileY + boundsY - 1) % boundsY]; + UpRight = bits[(TileX + 1) % boundsX, (TileY + boundsY - 1) % boundsY]; + DownLeft = bits[(TileX + boundsX - 1) % boundsX, (TileY + 1) % boundsY]; + DownRight = bits[(TileX + 1) % boundsX, (TileY + 1) % boundsY]; + break; + } + + int tile = tileDecider(); + tileOutput(tile); + tiles[TileX, TileY] = tile; + } + } + } + + return tiles; + } + + /* + The "mask" will also be used for tile checks! + A tile is solid if bits[x, y] OR mask[x, y] is solid + */ + public static int[,] Tile(bool[,] bits, bool[,] mask, Func tileDecider, Action tileOutput, int tileWidth, int tileHeight, EdgeBehavior edges) + { + int boundsX = bits.GetLength(0); + int boundsY = bits.GetLength(1); + int[,] tiles = new int[boundsX, boundsY]; + + for (TileX = 0; TileX < boundsX; TileX++) + { + for (TileY = 0; TileY < boundsY; TileY++) + { + if (bits[TileX, TileY]) + { + switch (edges) + { + case EdgeBehavior.True: + Left = TileX == 0 ? true : bits[TileX - 1, TileY] || mask[TileX - 1, TileY]; + Right = TileX == boundsX - 1 ? true : bits[TileX + 1, TileY] || mask[TileX + 1, TileY]; + Up = TileY == 0 ? true : bits[TileX, TileY - 1] || mask[TileX, TileY - 1]; + Down = TileY == boundsY - 1 ? true : bits[TileX, TileY + 1] || mask[TileX, TileY + 1]; + + UpLeft = (TileX == 0 || TileY == 0) ? true : bits[TileX - 1, TileY - 1] || mask[TileX - 1, TileY - 1]; + UpRight = (TileX == boundsX - 1 || TileY == 0) ? true : bits[TileX + 1, TileY - 1] || mask[TileX + 1, TileY - 1]; + DownLeft = (TileX == 0 || TileY == boundsY - 1) ? true : bits[TileX - 1, TileY + 1] || mask[TileX - 1, TileY + 1]; + DownRight = (TileX == boundsX - 1 || TileY == boundsY - 1) ? true : bits[TileX + 1, TileY + 1] || mask[TileX + 1, TileY + 1]; + break; + + case EdgeBehavior.False: + Left = TileX == 0 ? false : bits[TileX - 1, TileY] || mask[TileX - 1, TileY]; + Right = TileX == boundsX - 1 ? false : bits[TileX + 1, TileY] || mask[TileX + 1, TileY]; + Up = TileY == 0 ? false : bits[TileX, TileY - 1] || mask[TileX, TileY - 1]; + Down = TileY == boundsY - 1 ? false : bits[TileX, TileY + 1] || mask[TileX, TileY + 1]; + + UpLeft = (TileX == 0 || TileY == 0) ? false : bits[TileX - 1, TileY - 1] || mask[TileX - 1, TileY - 1]; + UpRight = (TileX == boundsX - 1 || TileY == 0) ? false : bits[TileX + 1, TileY - 1] || mask[TileX + 1, TileY - 1]; + DownLeft = (TileX == 0 || TileY == boundsY - 1) ? false : bits[TileX - 1, TileY + 1] || mask[TileX - 1, TileY + 1]; + DownRight = (TileX == boundsX - 1 || TileY == boundsY - 1) ? false : bits[TileX + 1, TileY + 1] || mask[TileX + 1, TileY + 1]; + break; + + case EdgeBehavior.Wrap: + Left = bits[(TileX + boundsX - 1) % boundsX, TileY] || mask[(TileX + boundsX - 1) % boundsX, TileY]; + Right = bits[(TileX + 1) % boundsX, TileY] || mask[(TileX + 1) % boundsX, TileY]; + Up = bits[TileX, (TileY + boundsY - 1) % boundsY] || mask[TileX, (TileY + boundsY - 1) % boundsY]; + Down = bits[TileX, (TileY + 1) % boundsY] || mask[TileX, (TileY + 1) % boundsY]; + + UpLeft = bits[(TileX + boundsX - 1) % boundsX, (TileY + boundsY - 1) % boundsY] || mask[(TileX + boundsX - 1) % boundsX, (TileY + boundsY - 1) % boundsY]; + UpRight = bits[(TileX + 1) % boundsX, (TileY + boundsY - 1) % boundsY] || mask[(TileX + 1) % boundsX, (TileY + boundsY - 1) % boundsY]; + DownLeft = bits[(TileX + boundsX - 1) % boundsX, (TileY + 1) % boundsY] || mask[(TileX + boundsX - 1) % boundsX, (TileY + 1) % boundsY]; + DownRight = bits[(TileX + 1) % boundsX, (TileY + 1) % boundsY] || mask[(TileX + 1) % boundsX, (TileY + 1) % boundsY]; + break; + } + + int tile = tileDecider(); + tileOutput(tile); + tiles[TileX, TileY] = tile; + } + } + } + + return tiles; + } + + public static int[,] Tile(bool[,] bits, AutotileData autotileData, Action tileOutput, int tileWidth, int tileHeight, EdgeBehavior edges) + { + return Tile(bits, autotileData.TileHandler, tileOutput, tileWidth, tileHeight, edges); + } + + public static int[,] Tile(bool[,] bits, bool[,] mask, AutotileData autotileData, Action tileOutput, int tileWidth, int tileHeight, EdgeBehavior edges) + { + return Tile(bits, mask, autotileData.TileHandler, tileOutput, tileWidth, tileHeight, edges); + } + + public static int TileX { get; private set; } + public static int TileY { get; private set; } + public static bool Left { get; private set; } + public static bool Right { get; private set; } + public static bool Up { get; private set; } + public static bool Down { get; private set; } + public static bool UpLeft { get; private set; } + public static bool UpRight { get; private set; } + public static bool DownLeft { get; private set; } + public static bool DownRight { get; private set; } + } + + public class AutotileData + { + public int[] Center; + public int[] Single; + public int[] SingleHorizontalLeft; + public int[] SingleHorizontalCenter; + public int[] SingleHorizontalRight; + public int[] SingleVerticalTop; + public int[] SingleVerticalCenter; + public int[] SingleVerticalBottom; + public int[] Top; + public int[] Bottom; + public int[] Left; + public int[] Right; + public int[] TopLeft; + public int[] TopRight; + public int[] BottomLeft; + public int[] BottomRight; + public int[] InsideTopLeft; + public int[] InsideTopRight; + public int[] InsideBottomLeft; + public int[] InsideBottomRight; + + public AutotileData(XmlElement xml) + { + Center = Calc.ReadCSVInt(xml.ChildText("Center", "")); + Single = Calc.ReadCSVInt(xml.ChildText("Single", "")); + + SingleHorizontalLeft = Calc.ReadCSVInt(xml.ChildText("SingleHorizontalLeft", "")); + SingleHorizontalCenter = Calc.ReadCSVInt(xml.ChildText("SingleHorizontalCenter", "")); + SingleHorizontalRight = Calc.ReadCSVInt(xml.ChildText("SingleHorizontalRight", "")); + + SingleVerticalTop = Calc.ReadCSVInt(xml.ChildText("SingleVerticalTop", "")); + SingleVerticalCenter = Calc.ReadCSVInt(xml.ChildText("SingleVerticalCenter", "")); + SingleVerticalBottom = Calc.ReadCSVInt(xml.ChildText("SingleVerticalBottom", "")); + + Top = Calc.ReadCSVInt(xml.ChildText("Top", "")); + Bottom = Calc.ReadCSVInt(xml.ChildText("Bottom", "")); + Left = Calc.ReadCSVInt(xml.ChildText("Left", "")); + Right = Calc.ReadCSVInt(xml.ChildText("Right", "")); + + TopLeft = Calc.ReadCSVInt(xml.ChildText("TopLeft", "")); + TopRight = Calc.ReadCSVInt(xml.ChildText("TopRight", "")); + BottomLeft = Calc.ReadCSVInt(xml.ChildText("BottomLeft", "")); + BottomRight = Calc.ReadCSVInt(xml.ChildText("BottomRight", "")); + + InsideTopLeft = Calc.ReadCSVInt(xml.ChildText("InsideTopLeft", "")); + InsideTopRight = Calc.ReadCSVInt(xml.ChildText("InsideTopRight", "")); + InsideBottomLeft = Calc.ReadCSVInt(xml.ChildText("InsideBottomLeft", "")); + InsideBottomRight = Calc.ReadCSVInt(xml.ChildText("InsideBottomRight", "")); + } + + public int TileHandler() + { + if (Tiler.Left && Tiler.Right && Tiler.Up && Tiler.Down && Tiler.UpLeft && Tiler.UpRight && Tiler.DownLeft && Tiler.DownRight) + return GetTileID(Center); + + else if (!Tiler.Up && !Tiler.Down) + { + if (Tiler.Left && Tiler.Right) + return GetTileID(SingleHorizontalCenter); + else if (!Tiler.Left && !Tiler.Right) + return GetTileID(Single); + else if (Tiler.Left) + return GetTileID(SingleHorizontalRight); + else + return GetTileID(SingleHorizontalLeft); + } + else if (!Tiler.Left && !Tiler.Right) + { + if (Tiler.Up && Tiler.Down) + return GetTileID(SingleVerticalCenter); + else if (Tiler.Down) + return GetTileID(SingleVerticalTop); + else + return GetTileID(SingleVerticalBottom); + } + + else if (Tiler.Up && Tiler.Down && Tiler.Left && !Tiler.Right) + return GetTileID(Right); + else if (Tiler.Up && Tiler.Down && !Tiler.Left && Tiler.Right) + return GetTileID(Left); + + else if (Tiler.Up && !Tiler.Left && Tiler.Right && !Tiler.Down) + return GetTileID(BottomLeft); + else if (Tiler.Up && Tiler.Left && !Tiler.Right && !Tiler.Down) + return GetTileID(BottomRight); + else if (Tiler.Down && Tiler.Right && !Tiler.Left && !Tiler.Up) + return GetTileID(TopLeft); + else if (Tiler.Down && !Tiler.Right && Tiler.Left && !Tiler.Up) + return GetTileID(TopRight); + + else if (Tiler.Up && Tiler.Down && !Tiler.DownRight && Tiler.DownLeft) + return GetTileID(InsideTopLeft); + else if (Tiler.Up && Tiler.Down && Tiler.DownRight && !Tiler.DownLeft) + return GetTileID(InsideTopRight); + else if (Tiler.Up && Tiler.Down && Tiler.UpLeft && !Tiler.UpRight) + return GetTileID(InsideBottomLeft); + else if (Tiler.Up && Tiler.Down && !Tiler.UpLeft && Tiler.UpRight) + return GetTileID(InsideBottomRight); + + else if (!Tiler.Down) + return GetTileID(Bottom); + else + return GetTileID(Top); + } + + private int GetTileID(int[] choices) + { + if (choices.Length == 0) + return -1; + else if (choices.Length == 1) + return choices[0]; + else + return Calc.Random.Choose(choices); + } + } +} diff --git a/MonocleEngineDemo/Monocle/Util/Tracker.cs b/MonocleEngineDemo/Monocle/Util/Tracker.cs new file mode 100644 index 0000000..2c1be77 --- /dev/null +++ b/MonocleEngineDemo/Monocle/Util/Tracker.cs @@ -0,0 +1,398 @@ +using Microsoft.Xna.Framework; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; + +namespace Monocle +{ + public class Tracker + { + #region Static + + public static Dictionary> TrackedEntityTypes { get; private set; } + public static Dictionary> TrackedComponentTypes { get; private set; } + public static Dictionary> TrackedCollidableComponentTypes { get; private set; } + public static HashSet StoredEntityTypes { get; private set; } + public static HashSet StoredComponentTypes { get; private set; } + public static HashSet StoredCollidableComponentTypes { get; private set; } + + public static void Initialize() + { + TrackedEntityTypes = new Dictionary>(); + TrackedComponentTypes = new Dictionary>(); + TrackedCollidableComponentTypes = new Dictionary>(); + StoredEntityTypes = new HashSet(); + StoredComponentTypes = new HashSet(); + StoredCollidableComponentTypes = new HashSet(); + + foreach (var type in Assembly.GetEntryAssembly().GetTypes()) + { + var attrs = type.GetCustomAttributes(typeof(Tracked), false); + if (attrs.Length > 0) + { + bool inherited = (attrs[0] as Tracked).Inherited; + + if (typeof(Entity).IsAssignableFrom(type)) + { + if (!type.IsAbstract) + { + if (!TrackedEntityTypes.ContainsKey(type)) + TrackedEntityTypes.Add(type, new List()); + TrackedEntityTypes[type].Add(type); + } + + StoredEntityTypes.Add(type); + + if (inherited) + { + foreach (var subclass in GetSubclasses(type)) + { + if (!subclass.IsAbstract) + { + if (!TrackedEntityTypes.ContainsKey(subclass)) + TrackedEntityTypes.Add(subclass, new List()); + TrackedEntityTypes[subclass].Add(type); + } + } + } + } + else if (typeof(Component).IsAssignableFrom(type)) + { + if (!type.IsAbstract) + { + if (!TrackedComponentTypes.ContainsKey(type)) + TrackedComponentTypes.Add(type, new List()); + TrackedComponentTypes[type].Add(type); + } + + StoredComponentTypes.Add(type); + + if (inherited) + { + foreach (var subclass in GetSubclasses(type)) + { + if (!subclass.IsAbstract) + { + if (!TrackedComponentTypes.ContainsKey(subclass)) + TrackedComponentTypes.Add(subclass, new List()); + TrackedComponentTypes[subclass].Add(type); + } + } + } + + if (typeof(CollidableComponent).IsAssignableFrom(type)) + { + if (!type.IsAbstract) + { + if (!TrackedCollidableComponentTypes.ContainsKey(type)) + TrackedCollidableComponentTypes.Add(type, new List()); + TrackedCollidableComponentTypes[type].Add(type); + } + + StoredCollidableComponentTypes.Add(type); + + if (inherited) + { + foreach (var subclass in GetSubclasses(type)) + { + if (!subclass.IsAbstract) + { + if (!TrackedCollidableComponentTypes.ContainsKey(subclass)) + TrackedCollidableComponentTypes.Add(subclass, new List()); + TrackedCollidableComponentTypes[subclass].Add(type); + } + } + } + } + } + else + throw new Exception("Type '" + type.Name + "' cannot be Tracked because it does not derive from Entity or Component"); + } + } + } + + private static List GetSubclasses(Type type) + { + List matches = new List(); + + foreach (var check in Assembly.GetEntryAssembly().GetTypes()) + if (type != check && type.IsAssignableFrom(check)) + matches.Add(check); + + return matches; + } + + #endregion + + public Dictionary> Entities { get; private set; } + public Dictionary> Components { get; private set; } + public Dictionary> CollidableComponents { get; private set; } + + public Tracker() + { + Entities = new Dictionary>(TrackedEntityTypes.Count); + foreach (var type in StoredEntityTypes) + Entities.Add(type, new List()); + + Components = new Dictionary>(TrackedComponentTypes.Count); + foreach (var type in StoredComponentTypes) + Components.Add(type, new List()); + + CollidableComponents = new Dictionary>(TrackedComponentTypes.Count); + foreach (var type in StoredCollidableComponentTypes) + CollidableComponents.Add(type, new List()); + } + + public bool IsEntityTracked() where T : Entity + { + return Entities.ContainsKey(typeof(T)); + } + + public bool IsComponentTracked() where T : Component + { + return Components.ContainsKey(typeof(T)) || CollidableComponents.ContainsKey(typeof(T)); + } + + public T GetEntity() where T : Entity + { +#if DEBUG + if (!IsEntityTracked()) + throw new Exception("Entity type '" + typeof(T).Name + "' is not marked with the Tracked attribute!"); +#endif + + var list = Entities[typeof(T)]; + if (list.Count == 0) + return null; + else + return list[0] as T; + } + + public T GetNearestEntity(Vector2 nearestTo) where T : Entity + { + var list = GetEntities(); + + T nearest = null; + float nearestDistSq = 0; + + foreach (T entity in list) + { + float distSq = Vector2.DistanceSquared(nearestTo, entity.Position); + + if (nearest == null || distSq < nearestDistSq) + { + nearest = entity; + nearestDistSq = distSq; + } + } + + return nearest; + } + + public List GetEntities() where T : Entity + { +#if DEBUG + if (!IsEntityTracked()) + throw new Exception("Entity type '" + typeof(T).Name + "' is not marked with the Tracked attribute!"); +#endif + + return Entities[typeof(T)]; + } + + public List GetEntitiesCopy() where T : Entity + { + return new List(GetEntities()); + } + + public IEnumerator EnumerateEntities() where T : Entity + { +#if DEBUG + if (!IsEntityTracked()) + throw new Exception("Entity type '" + typeof(T).Name + "' is not marked with the Tracked attribute!"); +#endif + + foreach (var e in Entities[typeof(T)]) + yield return e as T; + } + + public int CountEntities() where T : Entity + { +#if DEBUG + if (!IsEntityTracked()) + throw new Exception("Entity type '" + typeof(T).Name + "' is not marked with the Tracked attribute!"); +#endif + + return Entities[typeof(T)].Count; + } + + public T GetComponent() where T : Component + { +#if DEBUG + if (!IsComponentTracked()) + throw new Exception("Component type '" + typeof(T).Name + "' is not marked with the Tracked attribute!"); +#endif + + var list = Components[typeof(T)]; + if (list.Count == 0) + return null; + else + return list[0] as T; + } + + public T GetNearestComponent(Vector2 nearestTo) where T : Component + { + var list = GetComponents(); + + T nearest = null; + float nearestDistSq = 0; + + foreach (T component in list) + { + float distSq = Vector2.DistanceSquared(nearestTo, component.Entity.Position); + + if (nearest == null || distSq < nearestDistSq) + { + nearest = component; + nearestDistSq = distSq; + } + } + + return nearest; + } + + public List GetComponents() where T : Component + { +#if DEBUG + if (!IsComponentTracked()) + throw new Exception("Component type '" + typeof(T).Name + "' is not marked with the Tracked attribute!"); +#endif + + return Components[typeof(T)]; + } + + public List GetComponentsCopy() where T : Component + { + return new List(GetComponents()); + } + + public IEnumerator EnumerateComponents() where T : Component + { +#if DEBUG + if (!IsComponentTracked()) + throw new Exception("Component type '" + typeof(T).Name + "' is not marked with the Tracked attribute!"); +#endif + + foreach (var c in Components[typeof(T)]) + yield return c as T; + } + + public int CountComponents() where T : Component + { +#if DEBUG + if (!IsComponentTracked()) + throw new Exception("Component type '" + typeof(T).Name + "' is not marked with the Tracked attribute!"); +#endif + + return Components[typeof(T)].Count; + } + + internal void EntityAdded(Entity entity) + { + var type = entity.GetType(); + List trackAs; + + if (TrackedEntityTypes.TryGetValue(type, out trackAs)) + foreach (var track in trackAs) + Entities[track].Add(entity); + } + + internal void EntityRemoved(Entity entity) + { + var type = entity.GetType(); + List trackAs; + + if (TrackedEntityTypes.TryGetValue(type, out trackAs)) + foreach (var track in trackAs) + Entities[track].Remove(entity); + } + + internal void ComponentAdded(Component component) + { + var type = component.GetType(); + List trackAs; + + if (TrackedComponentTypes.TryGetValue(type, out trackAs)) + foreach (var track in trackAs) + Components[track].Add(component); + if (TrackedCollidableComponentTypes.TryGetValue(type, out trackAs)) + foreach (var track in trackAs) + CollidableComponents[track].Add(component as CollidableComponent); + } + + internal void ComponentRemoved(Component component) + { + var type = component.GetType(); + List trackAs; + + if (TrackedComponentTypes.TryGetValue(type, out trackAs)) + foreach (var track in trackAs) + Components[track].Remove(component); + if (TrackedCollidableComponentTypes.TryGetValue(type, out trackAs)) + foreach (var track in trackAs) + CollidableComponents[track].Remove(component as CollidableComponent); + } + + public void LogEntities() + { + if (Entities.Count == 0) + Engine.Commands.Log("n/a", Color.Red); + else + { + foreach (var kv in Entities) + { + string output = kv.Key.Name + " : " + kv.Value.Count; + Engine.Commands.Log(output); + } + } + } + + public void LogComponents() + { + if (Components.Count == 0) + Engine.Commands.Log("n/a", Color.Red); + else + { + foreach (var kv in Components) + { + string output = kv.Key.Name + " : " + kv.Value.Count; + Engine.Commands.Log(output); + } + } + } + + public void LogCollidableComponents() + { + if (CollidableComponents.Count == 0) + Engine.Commands.Log("n/a", Color.Red); + else + { + foreach (var kv in CollidableComponents) + { + string output = kv.Key.Name + " : " + kv.Value.Count; + Engine.Commands.Log(output); + } + } + } + } + + public class Tracked : Attribute + { + public bool Inherited; + + public Tracked(bool inherited = false) + { + Inherited = inherited; + } + } +} diff --git a/MonocleEngineDemo/Monocle/Util/VirtualMap.cs b/MonocleEngineDemo/Monocle/Util/VirtualMap.cs new file mode 100644 index 0000000..10d011f --- /dev/null +++ b/MonocleEngineDemo/Monocle/Util/VirtualMap.cs @@ -0,0 +1,121 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Monocle +{ + public class VirtualMap + { + public const int SegmentSize = 50; + public readonly int Columns; + public readonly int Rows; + public readonly int SegmentColumns; + public readonly int SegmentRows; + public readonly T EmptyValue; + + private T[,][,] segments; + + public VirtualMap(int columns, int rows, T emptyValue = default(T)) + { + Columns = columns; + Rows = rows; + SegmentColumns = (columns / SegmentSize) + 1; + SegmentRows = (rows / SegmentSize) + 1; + segments = new T[SegmentColumns, SegmentRows][,]; + EmptyValue = emptyValue; + } + + public VirtualMap(T[,] map, T emptyValue = default(T)) : this(map.GetLength(0), map.GetLength(1), emptyValue) + { + for (int x = 0; x < Columns; x++) + for (int y = 0; y < Rows; y++) + this[x, y] = map[x, y]; + } + + public bool AnyInSegmentAtTile(int x, int y) + { + int cx = x / SegmentSize; + int cy = y / SegmentSize; + + return segments[cx, cy] != null; + } + + public bool AnyInSegment(int segmentX, int segmentY) + { + return segments[segmentX, segmentY] != null; + } + + public T InSegment(int segmentX, int segmentY, int x, int y) + { + return segments[segmentX, segmentY][x, y]; + } + + public T[,] GetSegment(int segmentX, int segmentY) + { + return segments[segmentX, segmentY]; + } + + public T SafeCheck(int x, int y) + { + if (x >= 0 && y >= 0 && x < Columns && y < Rows) + return this[x, y]; + return EmptyValue; + } + + public T this[int x, int y] + { + get + { + int cx = x / SegmentSize; + int cy = y / SegmentSize; + + var seg = segments[cx, cy]; + if (seg == null) + return EmptyValue; + + return seg[x - cx * SegmentSize, y - cy * SegmentSize]; + } + + set + { + int cx = x / SegmentSize; + int cy = y / SegmentSize; + + if (segments[cx, cy] == null) + { + segments[cx, cy] = new T[SegmentSize, SegmentSize]; + + // fill with custom Empty Value data + if (EmptyValue != null && !EmptyValue.Equals(default(T))) + for (int tx = 0; tx < SegmentSize; tx++) + for (int ty = 0; ty < SegmentSize; ty++) + segments[cx, cy][tx, ty] = EmptyValue; + } + + segments[cx, cy][x - cx * SegmentSize, y - cy * SegmentSize] = value; + } + } + + public T[,] ToArray() + { + var array = new T[Columns, Rows]; + for (int x = 0; x < Columns; x++) + for (int y = 0; y < Rows; y++) + array[x, y] = this[x, y]; + return array; + } + + public VirtualMap Clone() + { + var clone = new VirtualMap(Columns, Rows, EmptyValue); + for (int x = 0; x < Columns; x++) + for (int y = 0; y < Rows; y++) + clone[x, y] = this[x, y]; + return clone; + } + + + } +} diff --git a/MonocleEngineDemo/MonocleDemo/Content/Atlases/testboxes.xml b/MonocleEngineDemo/MonocleDemo/Content/Atlases/testboxes.xml new file mode 100644 index 0000000..3f35144 --- /dev/null +++ b/MonocleEngineDemo/MonocleDemo/Content/Atlases/testboxes.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/MonocleEngineDemo/MonocleDemo/Content/Atlases/testboxes0.png b/MonocleEngineDemo/MonocleDemo/Content/Atlases/testboxes0.png new file mode 100644 index 0000000000000000000000000000000000000000..cbb900dd244add83d35759696623812340614654 GIT binary patch literal 426 zcmeAS@N?(olHy`uVBq!ia0vp^4h#%T3@pq*)`5od6+lWfz$e7@|4BX|^Z)<44o^ChrVqjo&@^oq#(?l^RnPcAqoh?-_{I{w1;OJV?-M^E8p$pEz@*bDJCEv4&2d2N@VX$bI0vJo}QKlC~i)i0|p@=d#Wzp$P!`?4MKs literal 0 HcmV?d00001 diff --git a/MonocleEngineDemo/MonocleDemo/Content/Content.mgcb b/MonocleEngineDemo/MonocleDemo/Content/Content.mgcb new file mode 100644 index 0000000..ddc4c36 --- /dev/null +++ b/MonocleEngineDemo/MonocleDemo/Content/Content.mgcb @@ -0,0 +1,15 @@ + +#----------------------------- Global Properties ----------------------------# + +/outputDir:bin/$(Platform) +/intermediateDir:obj/$(Platform) +/platform:DesktopGL +/config: +/profile:Reach +/compress:False + +#-------------------------------- References --------------------------------# + + +#---------------------------------- Content ---------------------------------# + diff --git a/MonocleEngineDemo/MonocleDemo/Content/spriteData/Notes.txt b/MonocleEngineDemo/MonocleDemo/Content/spriteData/Notes.txt new file mode 100644 index 0000000..fb1e711 --- /dev/null +++ b/MonocleEngineDemo/MonocleDemo/Content/spriteData/Notes.txt @@ -0,0 +1,62 @@ +/////////////////////////////////////////////////////////////////////// + +Notes regarding the Sprites.xml file used for creating a SpriteBank. + +/////////////////////////////////////////////////////////////////////// + + + + +Inside the 'Sprites' tag add all different sprites that you want to use. +Each of them can be used, for instance, for a different entity. +Each sprite has a 'path' attribute that represents the location or +folder inside the Atlas that we are accessing from a SpriteBank. That +attribute should be only the path represented by folders (not files). +Optionally, you can select a start animation for a sprite using the +'start' attribute (which is recommended) and naming the animation id. + +Inside the sprite there are several types of tags. +With the
tag, you can specify the sprite origin being in the +center of the sprite. +With the tag, you can set a specific point of +the sprite origin, tuning the x and y attributes in the range between +0 and 1 ( where (0,0) is the top-left and (1,1) is the bottom-right position). +There are two main types of tags that define the type of sprite: + and tags. +When a sprite has a tag, it means that once the sprite animation +has finished, it will start over. Unlike that, the tag defines that +the sprite animation when done remains as the last frame. +Both, the and tag, have an 'id' attribute which is the name of +that type of animation referenced by the sprite (like in the start attribute +of the sprite tag). The 'path' attribute represents the name of the file +located inside the sprite path. If the file is an animation consisting of +multiple frames, you can enter the name of the file ommiting the number of +the frame in the file name (for example, we have an animation of 8 frames, +where each frame has the name like 'arrow0', 'arrow1', 'arrow2' and so on. We +can just say path="arrow"). In case of multiple frames per animation, we can +also specify which frames at which order are the part of our animation, using +the frames attribute (for example: frames="0-7,9-10,2-4,15" will make an animation +going through the first seven frames, then skipping the eight and going from ninth +to tenth, then going from the second to the fourth and finally fifteenth frame of a file). +Instead of repeating a value several times just to make the animation, for example, frozen, +we can say frames="0*10", which means that it will repeat the frame with index 0 ten times. +If we assign this to an object, we will notice that the animation will go in an instant. +We can set the speed of the animation (in seconds) using the 'delay' attribute. The delay +will be set between each two frames we set in the 'frames' attribute. +Once the animation of the type was finished, we can change to another animation inside +the sprite using the 'goto' attribute and naming the id of the animation. You can also chain +multiple animations and once the current animation is over, it will execute them in the entered +order, starting from left to right. Additionally, you can also set the number of repeatings for +each of the provided animations using the format "idOfAnimation:numberOfRepeats" (for example: +goto="idle:10,sleep:6,wakeUp"). + +example: + + +
+ + + + + +/////////////////////////////////////////////////////////////////////// \ No newline at end of file diff --git a/MonocleEngineDemo/MonocleDemo/Content/spriteData/Sprites.xml b/MonocleEngineDemo/MonocleDemo/Content/spriteData/Sprites.xml new file mode 100644 index 0000000..b0bf9a0 --- /dev/null +++ b/MonocleEngineDemo/MonocleDemo/Content/spriteData/Sprites.xml @@ -0,0 +1,18 @@ + + + + + +
+ + + + + +
+ + + + \ No newline at end of file diff --git a/MonocleEngineDemo/MonocleDemo/FNA.dll b/MonocleEngineDemo/MonocleDemo/FNA.dll new file mode 100644 index 0000000000000000000000000000000000000000..600243845efa309deb4e56aa0c0ef0a11e365e24 GIT binary patch literal 941056 zcmd442YemHwLd=Edv%+Ny^?*ew6e9=2Af4!vW<Gt((~SX3rWj+2F>s9pgjm)h zNC-*j=^+Url=Pmwgyf}=gqIE>kV=RljdVf^?Z+1c5XhVD=qrBoLGCnl775F!5tB8#p)mK!vTv0vhl+&x1*Ee6dsHLT}ugUttb>X#F0X}Z6sTb>N z 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 y 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

agsf&FKs0{hRs71)37tHAzqe+BlRgB93+ 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 literal 0 HcmV?d00001 diff --git a/MonocleEngineDemo/MonocleDemo/GameEntities/Abstract/Dummy.cs b/MonocleEngineDemo/MonocleDemo/GameEntities/Abstract/Dummy.cs new file mode 100644 index 0000000..4923348 --- /dev/null +++ b/MonocleEngineDemo/MonocleDemo/GameEntities/Abstract/Dummy.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using MonocleDemoNamespace.Logic; +using Microsoft.Xna.Framework; +using Monocle; + +namespace MonocleDemoNamespace.GameEntities.Abstract +{ + public class Dummy: Entity + { + Sprite Sprite; + + public Dummy() + { + Add(Sprite = GFX.AllSprites.Create("player")); + Collider = new Hitbox(32, 32, -16, -16); + } + + public void Move(Vector2 add) + { + if (add.X != 0 || add.Y != 0) + { + Sprite.Play("move", false); + + // Move maximum in a direction until hitting a wall + + if (CollideCheck(GAccess.WallTag, new Vector2(X + add.X, Y))) + { + while (!CollideCheck(GAccess.WallTag, new Vector2(X + Calc.Sign(add).X, Y))) + X += Calc.Sign(add).X; + } + else + X += add.X* 60f * Engine.DeltaTime; + + if (CollideCheck(GAccess.WallTag, new Vector2(X, Y + add.Y))) + { + while (!CollideCheck(GAccess.WallTag, new Vector2(X, Y + Calc.Sign(add).Y))) + Y += Calc.Sign(add).Y; + } + else + Y += add.Y* 60f * Engine.DeltaTime; + } + } + + } +} diff --git a/MonocleEngineDemo/MonocleDemo/GameEntities/Abstract/SolidWall.cs b/MonocleEngineDemo/MonocleDemo/GameEntities/Abstract/SolidWall.cs new file mode 100644 index 0000000..3f4339f --- /dev/null +++ b/MonocleEngineDemo/MonocleDemo/GameEntities/Abstract/SolidWall.cs @@ -0,0 +1,29 @@ +using MonocleDemoNamespace.Logic; +using Microsoft.Xna.Framework; +using Monocle; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace MonocleDemoNamespace.GameEntities.Abstract +{ + public class SolidWall: Entity + { + Sprite Sprite; // Entity sprite + + public SolidWall() + { + Add(Sprite = GFX.AllSprites.Create("wall")); + Collider = new Hitbox(32, 32, -16, -16); + Tag = GAccess.WallTag; + } + + public SolidWall(Vector2 pos) : this() + { + Position = pos; + } + + } +} diff --git a/MonocleEngineDemo/MonocleDemo/Icon.bmp b/MonocleEngineDemo/MonocleDemo/Icon.bmp new file mode 100644 index 0000000000000000000000000000000000000000..2b481653e241818d2894e4ef33234eaff9aad701 GIT binary patch literal 262282 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 RJoIeLznU7AhJ`o~_y6Yy51#-4 literal 0 HcmV?d00001 diff --git a/MonocleEngineDemo/MonocleDemo/Logic/GAccess.cs b/MonocleEngineDemo/MonocleDemo/Logic/GAccess.cs new file mode 100644 index 0000000..5918cca --- /dev/null +++ b/MonocleEngineDemo/MonocleDemo/Logic/GAccess.cs @@ -0,0 +1,24 @@ +using Monocle; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace MonocleDemoNamespace.Logic +{ + ///

+ /// Global Access class + /// + public static class GAccess + { + #region Game Tags + + public static BitTag WallTag = new BitTag("wall"); + public static BitTag ItemTag = new BitTag("item"); + + #endregion + + } + +} diff --git a/MonocleEngineDemo/MonocleDemo/Logic/GFX.cs b/MonocleEngineDemo/MonocleDemo/Logic/GFX.cs new file mode 100644 index 0000000..3e75ead --- /dev/null +++ b/MonocleEngineDemo/MonocleDemo/Logic/GFX.cs @@ -0,0 +1,66 @@ +using Monocle; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace MonocleDemoNamespace.Logic +{ + public class GFX + { + + #region Fields + + private static SpriteBank _spriteBank = null; + List Atlases; + + public static SpriteBank AllSprites + { + get + { + return _spriteBank; + } + } + + #endregion + + #region Singleton + + private static GFX _instance = null; + + public static GFX Instance + { + get + { + if (_instance == null) + _instance = new GFX(); + return _instance; + } + } + + private GFX() + { + InitStuff(); + } + + #endregion + + private void InitStuff() + { + Atlases = new List(); + Atlases.Add(Atlas.FromAtlas("Atlases\\testboxes", Atlas.AtlasDataFormat.CrunchXmlOrBinary)); + + _spriteBank = new SpriteBank(Atlases[0], "spriteData\\Sprites.xml"); + } + + /// + /// Reloads atlases from file (should not be called so often). + /// + public void RefreshData() + { + InitStuff(); + } + + } +} diff --git a/MonocleEngineDemo/MonocleDemo/MonoDemo.cs b/MonocleEngineDemo/MonocleDemo/MonoDemo.cs new file mode 100644 index 0000000..c2b0a3a --- /dev/null +++ b/MonocleEngineDemo/MonocleDemo/MonoDemo.cs @@ -0,0 +1,101 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using MonocleDemoNamespace.Logic; +using MonocleDemoNamespace.Scenes.Menus; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Audio; +using Microsoft.Xna.Framework.Content; +using Microsoft.Xna.Framework.Graphics; +using Microsoft.Xna.Framework.Input; +using Microsoft.Xna.Framework.Media; +using Monocle; + +namespace MonocleDemoNamespace +{ + /// + /// This is the main type for your game. + /// + public class MonoDemo : Engine + { + public MonoDemo() : base(1280, 720, 640, 360, "MonocleEngine Test", false) + { + Engine.ClearColor = Color.CornflowerBlue; + Content.RootDirectory = "Content"; + ViewPadding = -32; + } + + /// + /// Allows the game to perform any initialization it needs to before starting to run. + /// This is where it can query for any required services and load any non-graphic + /// related content. Calling base.Initialize will enumerate through any components + /// and initialize them as well. + /// + protected override void Initialize() + { + // TODO: Add your initialization logic here + + // Initialize some components + { + int initGAccess = GAccess.WallTag.ID; + GFX graphicsInit = GFX.Instance; + } + + base.Initialize(); + InitScene isc = new InitScene(); + Scene = isc; + } + + /// + /// LoadContent will be called once per game and is the place to load + /// all of your content. + /// + protected override void LoadContent() + { + // Create a new SpriteBatch, which can be used to draw textures. + + + // TODO: use this.Content to load your game content here + base.LoadContent(); + + //SetFullscreen(); + + } + + /// + /// UnloadContent will be called once per game and is the place to unload + /// game-specific content. + /// + protected override void UnloadContent() + { + // TODO: Unload any non ContentManager content here + } + + /// + /// Allows the game to run logic such as updating the world, + /// checking for collisions, gathering input, and playing audio. + /// + /// Provides a snapshot of timing values. + protected override void Update(GameTime gameTime) + { + if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape)) + Exit(); + + // TODO: Add your update logic here + + base.Update(gameTime); + } + + /// + /// This is called when the game should draw itself. + /// + /// Provides a snapshot of timing values. + protected override void Draw(GameTime gameTime) + { + base.Draw(gameTime); + + + // TODO: Add your drawing code here + } + } +} diff --git a/MonocleEngineDemo/MonocleDemo/Monocle.dll b/MonocleEngineDemo/MonocleDemo/Monocle.dll new file mode 100644 index 0000000000000000000000000000000000000000..a5dc4efefaf6dd7dcc63f3ff30267ea633444e6c GIT binary patch literal 276480 zcmd>n34C2;nfJ*#=PtQ7NqchBn>HvBHS{@}qMZQJ_b!52Pl z=j8GmCZeZKTz=j1D=)wP`sk+RS6sb(;^ym@@4SBbN#|U!{JQ9>t5vF?HsP+f}?7V3Ik`n-Gt1{&~)h z=P@b&C12xZ5dK~OxaZITxSt>+n*VnBP6xuyJ==GdG{?>6U+y@?RD2Qs(^CFbH(h<} zP4NHc5r!_(YVfwnkY&fJiOGp85g@pccKmbJ@+bM*3~{ZRxcb@%35lx%oCVhUY3WW# za;fVRnJ?Ajc3$?NQQ{t3rWdPGog0*=; zW6W{G8{kp0{J!wxIaD2?-xC^81pq6R#tR2+=E~|+8ZVtFL}k?iD$wIaz-c-hKdjkA zh-bQW*>qk+%)(_c*FBNjh~jEIWBYs|ggo!G`8_u}B?J4h#~lZls1e{n6uC}*ubt+$jP^sD9CQrBBm_3HnCl!ING&Wnx*0<|{8It8oShXPhkW%Es>ExCS!U)KwJ zk+|&U#arm8ImH3e0~(sk{vi@(%0A4iFJQTgy~t~G@=Zu=${<0QGvfd$HQ}%r>5l~t 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º=FepCkOR0_%jCH-kzLDw$ttiO{ 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 + + + + Debug + AnyCPU + 8.0.30703 + 2.0 + {1A6DD559-1A6A-437C-9321-76D9D0BB2CA0} + WinExe + Properties + MonocleDemoNamespace + MonocleDemo + 512 + DesktopGL + v4.5.2 + + + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + false + true + + + true + bin\$(MonoGamePlatform)\$(Platform)\$(Configuration)\ + DEBUG;TRACE;LINUX + full + AnyCPU + prompt + false + 4 + + + bin\$(MonoGamePlatform)\$(Platform)\$(Configuration)\ + TRACE;LINUX + true + pdbonly + AnyCPU + prompt + false + 4 + + + Icon.ico + + + app.manifest + + + + + + + + + + + + + + + $(MonoGameInstallDirectory)\MonoGame\v3.0\Assemblies\DesktopGL\MonoGame.Framework.dll + + + + + + + + + + + + x86\SDL2.dll + PreserveNewest + + + x64\SDL2.dll + PreserveNewest + + + x86\soft_oal.dll + PreserveNewest + + + x64\soft_oal.dll + PreserveNewest + + + x86\libSDL2-2.0.so.0 + PreserveNewest + + + x64\libSDL2-2.0.so.0 + PreserveNewest + + + x86\libopenal.so.1 + PreserveNewest + + + x64\libopenal.so.1 + PreserveNewest + + + libSDL2-2.0.0.dylib + PreserveNewest + + + libopenal.1.dylib + PreserveNewest + + + MonoGame.Framework.dll.config + PreserveNewest + + + + + + + Always + + + Always + + + + Always + + + + + {6ec3c0a6-c5be-42ce-aea5-881d8a97eb60} + Monocle.MonoGame + + + + + False + Microsoft .NET Framework 4.5.2 %28x86 and x64%29 + true + + + False + .NET Framework 3.5 SP1 + false + + + + + + \ No newline at end of file diff --git a/MonocleEngineDemo/MonocleDemo/Program.cs b/MonocleEngineDemo/MonocleDemo/Program.cs new file mode 100644 index 0000000..f7c7ca2 --- /dev/null +++ b/MonocleEngineDemo/MonocleDemo/Program.cs @@ -0,0 +1,24 @@ +using System; +using MonocleDemoNamespace.Logic; +using Monocle; + +namespace MonocleDemoNamespace +{ + /// + /// The main class. + /// + public static class Program + { + /// + /// The main entry point for the application. + /// + [STAThread] + static void Main() + { + using (var game = new MonoDemo()) + { + game.Run(); + } + } + } +} diff --git a/MonocleEngineDemo/MonocleDemo/Properties/AssemblyInfo.cs b/MonocleEngineDemo/MonocleDemo/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..ea7c9c6 --- /dev/null +++ b/MonocleEngineDemo/MonocleDemo/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +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("MonocleDemoNamespace")] +[assembly: AssemblyProduct("MonocleDemoNamespace")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyCompany("Microsoft")] +[assembly: AssemblyCopyright("Copyright © Microsoft 2019")] +[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("4879dc03-2a08-42b9-b350-6e426a4ede08")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/MonocleEngineDemo/MonocleDemo/Scenes/Menus/InitScene.cs b/MonocleEngineDemo/MonocleDemo/Scenes/Menus/InitScene.cs new file mode 100644 index 0000000..528a738 --- /dev/null +++ b/MonocleEngineDemo/MonocleDemo/Scenes/Menus/InitScene.cs @@ -0,0 +1,130 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Monocle; +using MonocleDemoNamespace.GameEntities.Abstract; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Input; + +namespace MonocleDemoNamespace.Scenes.Menus +{ + public class InitScene: Scene + { + Camera camera; // Camera handler (so that we can modify it's paramteres when needed, like zoom) + Dummy dum; // Dummy + int dummyMove = 4; // Dummy movement speed + + public InitScene() : base() + {} + + public override void Begin() + { + base.Begin(); + + // Add the EverythingRenderer object so it will actually display the contents of the scene + EverythingRenderer er = new EverythingRenderer(); + Add(er); + + // Setup the camera + camera = new Camera(640, 360); + camera.CenterOrigin(); + camera.Zoom = 1.5f; + er.Camera = camera; // Attach the new camera to the EverythingRenderer + + // Create the dummy + dum = new Dummy(); + Vector2 screenHalf = (new Vector2(Engine.Width, Engine.Height))/2; + dum.Position = screenHalf; + Add(dum); + + // Create walls (mainly to limit the movement of the dummy and to test collisions) + CreateWallBorder(); + AdditionalWalls(); + } + + private void CreateWallBorder() + { + for (int i = 16; i < Engine.Width; i += 32) + { + SolidWall sw = new SolidWall(new Vector2(i, 0)); + Add(sw); + sw = new SolidWall(new Vector2(i, Engine.Height - 16)); + Add(sw); + } + + for (int i = 0; i < Engine.Height; i += 32) + { + SolidWall sw = new SolidWall(new Vector2(-16, i)); + Add(sw); + sw = new SolidWall(new Vector2(Engine.Width - 16, i)); + Add(sw); + } + + + } + + private void AdditionalWalls() + { + // Above and bellow the player + for (int i = 0; i < 3; i++) + { + Vector2 pos = new Vector2(dum.X, dum.Y - 32 * (i + 2)); + Vector2 pos2 = new Vector2(dum.X, dum.Y + 32 * (i + 2)); + SolidWall sw = new SolidWall(pos); + SolidWall sw2 = new SolidWall(pos2); + Add(sw); + Add(sw2); + } + + // Left and right to the player + for (int i = 0; i < 5; i++) + { + Vector2 pos = new Vector2(dum.X - 32 * (i + 3), dum.Y); + Vector2 pos2 = new Vector2(dum.X + 32 * (i + 3), dum.Y); + SolidWall sw = new SolidWall(pos); + SolidWall sw2 = new SolidWall(pos2); + Add(sw); + Add(sw2); + } + + } + + public override void Update() + { + base.Update(); + + int nx = dummyMove * MInput.Keyboard.AxisCheck(Keys.Left, Keys.Right); + int ny = dummyMove * MInput.Keyboard.AxisCheck(Keys.Up, Keys.Down); + + dum.Move( new Vector2(nx, ny)); + + // This is a direct transition to test scene + if (MInput.Keyboard.Check(Keys.LeftShift,Keys.RightShift) && MInput.Keyboard.Check(Keys.Add)) + Engine.Scene = new TestScene(); + } + + public override void AfterUpdate() + { + base.AfterUpdate(); + Vector2 tresh = new Vector2(120, 90); // Padding (horizontal,vertical) + Vector2 npos = new Vector2(dum.X - Engine.ViewWidth / 4, dum.Y - Engine.ViewHeight / 4); + + // Clamp camera position + if (npos.X > Engine.Width / 2 + tresh.X) + npos.X = Math.Min(npos.X, Engine.Width / 2 + tresh.X); + else if (npos.X < Engine.Width / 2 - tresh.X) + npos.X = Math.Max(npos.X, tresh.X / 2); + + if (npos.Y > Engine.Height / 2 + tresh.Y) + npos.Y = Math.Min(npos.Y, Engine.Height / 2 + tresh.Y); + else if (npos.Y < Engine.Height / 2 - tresh.Y) + npos.Y = Math.Max(npos.Y, tresh.Y / 2); + + camera.Position = npos; + } + + + } +} diff --git a/MonocleEngineDemo/MonocleDemo/Scenes/Menus/TestScene.cs b/MonocleEngineDemo/MonocleDemo/Scenes/Menus/TestScene.cs new file mode 100644 index 0000000..cf15fa3 --- /dev/null +++ b/MonocleEngineDemo/MonocleDemo/Scenes/Menus/TestScene.cs @@ -0,0 +1,24 @@ +using Monocle; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace MonocleDemoNamespace.Scenes.Menus +{ + public class TestScene: Scene + { + + public override void Begin() + { + base.Begin(); + + var renderer = new EverythingRenderer(); + Add(renderer); + + + } + + } +} diff --git a/MonocleEngineDemo/MonocleDemo/app.config b/MonocleEngineDemo/MonocleDemo/app.config new file mode 100644 index 0000000..ff99501 --- /dev/null +++ b/MonocleEngineDemo/MonocleDemo/app.config @@ -0,0 +1,3 @@ + + + diff --git a/MonocleEngineDemo/MonocleDemo/app.manifest b/MonocleEngineDemo/MonocleDemo/app.manifest new file mode 100644 index 0000000..408b23f --- /dev/null +++ b/MonocleEngineDemo/MonocleDemo/app.manifest @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true/pm + + + + diff --git a/MonocleEngineDemo/MonocleEngineDemo.sln b/MonocleEngineDemo/MonocleEngineDemo.sln new file mode 100644 index 0000000..6c812c0 --- /dev/null +++ b/MonocleEngineDemo/MonocleEngineDemo.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.27703.2035 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MonocleDemo", "MonocleDemo\MonocleDemo.csproj", "{1A6DD559-1A6A-437C-9321-76D9D0BB2CA0}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Monocle.MonoGame", "Monocle\Monocle.MonoGame.csproj", "{6EC3C0A6-C5BE-42CE-AEA5-881D8A97EB60}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {1A6DD559-1A6A-437C-9321-76D9D0BB2CA0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1A6DD559-1A6A-437C-9321-76D9D0BB2CA0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1A6DD559-1A6A-437C-9321-76D9D0BB2CA0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1A6DD559-1A6A-437C-9321-76D9D0BB2CA0}.Release|Any CPU.Build.0 = Release|Any CPU + {6EC3C0A6-C5BE-42CE-AEA5-881D8A97EB60}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6EC3C0A6-C5BE-42CE-AEA5-881D8A97EB60}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6EC3C0A6-C5BE-42CE-AEA5-881D8A97EB60}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6EC3C0A6-C5BE-42CE-AEA5-881D8A97EB60}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {B9E28CA4-002D-4B1C-8139-01B44CBA23BC} + EndGlobalSection +EndGlobal diff --git a/README.md b/README.md index 2ae55d5..a808607 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,6 @@ # MonocleEngineDemo A working project made using MonocleEngine by Matt Thorson and MonoGame, covering basing things. + +This project contains the project, MonocleEngine (https://bitbucket.org/MattThorson/monocle-engine/src/default/) source and a built version of Cevyray's Crunch project (https://github.com/chevyray/crunch), along with some sprites for demonstration purposes. + +It contains two scenes, entities, loading atlases from generated xml crunch files, creating a spritebank and using it inside entities, basic collisions, camera adjustments and object movement using MInput. \ No newline at end of file diff --git a/Sources/Atlases/testboxes.xml b/Sources/Atlases/testboxes.xml new file mode 100644 index 0000000..3f35144 --- /dev/null +++ b/Sources/Atlases/testboxes.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Sources/Atlases/testboxes0.png b/Sources/Atlases/testboxes0.png new file mode 100644 index 0000000000000000000000000000000000000000..cbb900dd244add83d35759696623812340614654 GIT binary patch literal 426 zcmeAS@N?(olHy`uVBq!ia0vp^4h#%T3@pq*)`5od6+lWfz$e7@|4BX|^Z)<44o^ChrVqjo&@^oq#(?l^RnPcAqoh?-_{I{w1;OJV?-M^E8p$pEz@*bDJCEv4&2d2N@VX$bI0vJo}QKlC~i)i0|p@=d#Wzp$P!`?4MKs literal 0 HcmV?d00001 diff --git a/Sources/Graphics/testing/base_cubes/arrow0.png b/Sources/Graphics/testing/base_cubes/arrow0.png new file mode 100644 index 0000000000000000000000000000000000000000..860ea96b78f4a87c1f66ee075025b16017fc5a02 GIT binary patch literal 190 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}Ea{HEjtmSN z`?>!lvI6;>1s;*b3=DjSK$uZf!>a)(80_id7!u+B_S#-9pi!-X`S1Uq``f_5x6@@u zO}S>qiwRAc?;?`h*5@(;6$8N~lPOPj&za9`9QUB?r)TLmsVW7A(7E;B7~+k<@)vjv Yq8U0wjeh*hZ2}2+y85}Sb4q9e0I~o%bN~PV literal 0 HcmV?d00001 diff --git a/Sources/Graphics/testing/base_cubes/arrow1.png b/Sources/Graphics/testing/base_cubes/arrow1.png new file mode 100644 index 0000000000000000000000000000000000000000..64e62b208111b4c825e496c98e633ebd305a4707 GIT binary patch literal 191 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}Ea{HEjtmSN z`?>!lvI6;>1s;*b3=DjSK$uZf!>a)(7~<*T7!u+B_S$JKpiwS?%m4p>E~nts&EZ&3 zoF3f0z?o~=&g5RnecM1L0KtxB3svs_4K{Uo%xW}!e#-g%PW&ti(U(`rJ@5v(gMncd ZV;XnIR#79H4WjKJzNf37%Q~loCIBoNHf8_- literal 0 HcmV?d00001 diff --git a/Sources/Graphics/testing/base_cubes/arrow2.png b/Sources/Graphics/testing/base_cubes/arrow2.png new file mode 100644 index 0000000000000000000000000000000000000000..5091bf5efd51b9d7b6e14bb3a6acd497c00bc2eb GIT binary patch literal 188 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}Ea{HEjtmSN z`?>!lvI6;>1s;*b3=DjSK$uZf!>a)(80hKZ7!u+B_L?IX&?uL{<-hOW62`tMyESw)`R$-u6{1-oD!M<9+x>3 literal 0 HcmV?d00001 diff --git a/Sources/Graphics/testing/base_cubes/arrow3.png b/Sources/Graphics/testing/base_cubes/arrow3.png new file mode 100644 index 0000000000000000000000000000000000000000..c4e4b5055b128cd45a2ece97180ca12c3655ee40 GIT binary patch literal 185 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}Ea{HEjtmSN z`?>!lvI6;>1s;*b3=DjSK$uZf!>a)(=;!I;7!u+B_L?IX&?uL{<-hOW!lvI6;>1s;*b3=DjSK$uZf!>a)(nC0o>7!u+BcA7UA&?wfezwhr9^72{~_Uw?k zz{OX*xB9c&nKYC6n3)7lC>}UtrSp!-?8>_0ca2-Q-Z8D8ZRgJI7dj*NM%>}ch96xP zZDrbjnDykDjqG_V4!qt`p)m8uBu54&fd&SaCe{wAlk=n<^*pxh0NTpn>FVdQ&MBb@ E0Og8C3;+NC literal 0 HcmV?d00001 diff --git a/Sources/Graphics/testing/base_cubes/arrow5.png b/Sources/Graphics/testing/base_cubes/arrow5.png new file mode 100644 index 0000000000000000000000000000000000000000..2603f222a07e6985e43be24a61f108a48aa672e9 GIT binary patch literal 236 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}Ea{HEjtmSN z`?>!lvI6;>1s;*b3=DjSK$uZf!>a)(*y!ov7!u+BcA7WW0Rs+~t>5<-M=MS>ahZHJ zWP=???78GK%WLihD7gP_V-!wEilb$E1bA#Q`%t<=VdnoE%yL>Atn@fkHrJi>TEGHA3K0!2 Zm~>96IC2=?+Xr+EgQu&X%Q~loCIF~bPCx(v literal 0 HcmV?d00001 diff --git a/Sources/Graphics/testing/base_cubes/arrow6.png b/Sources/Graphics/testing/base_cubes/arrow6.png new file mode 100644 index 0000000000000000000000000000000000000000..9c55536e1fc5b7723140c0e6e9116cd66f517efd GIT binary patch literal 263 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}Ea{HEjtmSN z`?>!lvI6;>1s;*b3=DjSK$uZf!>a)(ILFh)F(ktM?esvd0}4FOx9|QxcbjEu)RwR6 zVprIc->g>jynF0DKkKI_)e;7bXJYiZI~Z)Q2yM(~nKhAHfN}BBSDiP^n!lvI6;>1s;*b3=DjSK$uZf!>a)(xWd!LF(ktM>2z6P0C<(&+I3cA-kUG8%1(Q}afaHmjH z@QmI8)(E{TvWI3)=C*BnnyV2jrm^Eofx<3}!>0cYnWdZ?w(4mdvOBp%lDpGEYhR9| zSOH_ycb_ii&o>-dEA-4~Fr5D7cUJLB+4&aB0VTrFE#} R380%9JYD@<);T3K0RYcyVRQfh literal 0 HcmV?d00001 diff --git a/Sources/Graphics/testing/base_cubes/black_base.png b/Sources/Graphics/testing/base_cubes/black_base.png new file mode 100644 index 0000000000000000000000000000000000000000..a9d8c6554d895a44e8cfe0d0572e5dd58705605d GIT binary patch literal 159 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}Ea{HEjtmSN z`?>!lvI6;>1s;*b3=DjSK$uZf!>a)(XyWPO7!u+B_S{BZ1_d5w$M(PFei}CvS-($u n`c2F8{@T)e>p^-E;Dfb-q@oa`y*3Yf6G+t4)z4*}Q$iB}O2j6! literal 0 HcmV?d00001 diff --git a/Sources/Graphics/testing/base_cubes/blue_base.png b/Sources/Graphics/testing/base_cubes/blue_base.png new file mode 100644 index 0000000000000000000000000000000000000000..f7ea527561418d77659f40202e8cbf735b532016 GIT binary patch literal 160 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}Ea{HEjtmSN z`?>!lvI6;>1s;*b3=DinK$vl=HlH+5(A3k#F(ktM?Kwk61_hoY2lW4iE?B3RaL}1u p&->cyH+GUad;39}5#WQh!p`^mm`)zqzj+5p)YH|^Wt~$(69AaPDyskh literal 0 HcmV?d00001 diff --git a/Sources/Graphics/testing/base_cubes/green_base.png b/Sources/Graphics/testing/base_cubes/green_base.png new file mode 100644 index 0000000000000000000000000000000000000000..5ca957917821b4548ca46320bbc9e14fcbfd121d GIT binary patch literal 162 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}Ea{HEjtmSN z`?>!lvI6;>1s;*b3=DinK$vl=HlH+5(A?9-F(ktM?Kwk61_hoY2iPSm4(u0sa)Fg! sc85-6{+rEtQnTOZG6Gd1z#l#jF6He^v96_m?t(-;UHx3vIVCg!0J9({;{X5v literal 0 HcmV?d00001 diff --git a/Sources/Graphics/testing/base_cubes/red_base.png b/Sources/Graphics/testing/base_cubes/red_base.png new file mode 100644 index 0000000000000000000000000000000000000000..ff3fbd004f6480032e3ed326a1117296a0b47fb1 GIT binary patch literal 162 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}Ea{HEjtmSN z`?>!lvI6;>1s;*b3=DinK$vl=HlH+5(A?9-F(ktM?Kwq81_c3@gUvD?zs!}H^Iv>u siridwEb{lVjX&>+0Tm*^f%gqd7{u(Da;805J{2VF>FVdQ&MBb@0RHwXM*si- literal 0 HcmV?d00001 diff --git a/Sources/Graphics/testing/base_cubes/white_base.png b/Sources/Graphics/testing/base_cubes/white_base.png new file mode 100644 index 0000000000000000000000000000000000000000..174204e74a574aa74c84c50e859b6c1f3d633170 GIT binary patch literal 151 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1SJ1Ryj={W7>k44ofy`glX(f`u%tWsIx;Y9 z?C1WI$O`0h7I;J!GcfQS24TkI`72U@f;yfqjv*f2Z_gSsG8pi%Ec|8ucj|#DZw(II h-pHi202w___}v})WtqYETeU!a44$rjF6*2UngA2UEk^(V literal 0 HcmV?d00001 diff --git a/Sources/Graphics/testing/base_cubes/yellow_base.png b/Sources/Graphics/testing/base_cubes/yellow_base.png new file mode 100644 index 0000000000000000000000000000000000000000..d08949d7f1b14d39b034f34dac8a8fda1a1d59d3 GIT binary patch literal 161 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}Ea{HEjtmSN z`?>!lvI6;>1s;*b3=DinK$vl=HlH+5(9F}tF(ktM?Kwk61_hoY2iO^ZY;Re$>54PE rp7*urQvcKqcjdW(>JZ>Txx*)hU8k5u510IY4-)ot^>bP0l+XkKvob1V literal 0 HcmV?d00001 diff --git a/Tools/Crunch/crunch.exe b/Tools/Crunch/crunch.exe new file mode 100644 index 0000000000000000000000000000000000000000..073b49ee656a1754291d90bf5f4357f704303013 GIT binary patch literal 168960 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%0D-d<^W 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 F{tscENBaN( literal 0 HcmV?d00001 diff --git a/Tools/Crunch/readme.md b/Tools/Crunch/readme.md new file mode 100644 index 0000000..a5a7886 --- /dev/null +++ b/Tools/Crunch/readme.md @@ -0,0 +1,120 @@ +# crunch + +This is a command line tool that will pack a bunch of images into a single, larger image. It was designed for [Celeste](http://www.celestegame.com/), but could be very helpful for other games. + +It is designed using libraries with permissible licenses, so you are able to use it freely in your commercial and non-commercial projects. Please see each source file for its respective copyright and license. + +### Features + +- Export XML, JSON, or binary data +- Trim excess transparency +- Rotate images to fit +- Control atlas size and padding +- Premultiply pixel values +- Recursively scans folders +- Remove duplicate images +- Caching to prevent redundant builds +- Multi-image atlas when the sprites don't fit + +### What does it do? + +Given a folder with several images, like so: + +``` +images/ + player.png + tree.png + enemy.png +``` + +It will output something like this: + +``` +bin/ + images.png + images.xml + images.hash +``` + +Where `images.png` is the packed image, `images.xml` is an xml file describing where each sub-image is located, and `images.hash` is used for file caching (if none of the input files have changed since the last pack, the program will terminate). + +There is also an option to use a binary format instead of xml. + +### Usage + +`crunch [OUTPUT] [INPUT1,INPUT2,INPUT3...] [OPTIONS...]` + +For example... + +`crunch bin/atlases/atlas assets/characters,assets/tiles -p -t -v -u -r -j` + +This will output the following files: + +``` +bin/atlases/atlas.png +bin/atlases/atlas.json +bin/atlases/atlas.hash +``` + +### Options + +| option | alias | description | +| ------------- | ------------- | ------------| +| -d | --default | use default settings (-x -p -t -u) +| -x | --xml | saves the atlas data as a .xml file +| -b | --binary | saves the atlas data as a .bin file +| -j | --json | saves the atlas data as a .json file +| -p | --premultiply | premultiplies the pixels of the bitmaps by their alpha channel +| -t | --trim | trims excess transparency off the bitmaps +| -v | --verbose | print to the debug console as the packer works +| -f | --force | ignore caching, forcing the packer to repack +| -u | --unique | remove duplicate bitmaps from the atlas +| -r | --rotate | enabled rotating bitmaps 90 degrees clockwise when packing +| -s# | --size# | max atlas size (# can be 4096, 2048, 1024, 512, 256, 128, or 64) +| -p# | --pad# | padding between images (# can be from 0 to 16) + +### Binary Format + + ``` + [int16] num_textures (below block is repeated this many times) + [string] name + [int16] num_images (below block is repeated this many times) + [string] img_name + [int16] img_x + [int16] img_y + [int16] img_width + [int16] img_height + [int16] img_frame_x (if --trim enabled) + [int16] img_frame_y (if --trim enabled) + [int16] img_frame_width (if --trim enabled) + [int16] img_frame_height (if --trim enabled) + [byte] img_rotated (if --rotate enabled) +``` + +### License + +Unless otherwise specified in a source file, everything in this project falls under the following license: + +``` +MIT License + +Copyright (c) 2017 Chevy Ray Johnston + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +```